Set.of(E... elements) - 它使用的是哪个 Set 实现?它与 new HashSet<>() 有什么关系?

Set.of(E... elements) - which Set implementation is it using? and what is its relation to new HashSet<>()?

我想问一下 - 和

有什么区别
Set<CurrencyType> set1 = new HashSet<>() {{
    add("a");
    add("b");
    add("c");
}}

Set<CurrencyType> set2 = Set.of(
    "a",
    "b",
    "c"
)

在名为 MySillyTest 的 @Test 的调试模式下(这个名称很快就会恢复)我可以看到 set1MySillyTest 的一个实例,但我假设它只是一个HashSet。另一方面,set2ImmutableCollections$SetN 的一个实例。这两者之间的真正区别是什么? Set.of() 使用的是 java.util.Set 的什么实现?这两者之间有 performance/memory usage/cpu 用法差异吗?

不保证 Set.of(...) 的实现 class。它可能会根据运行时实现或未来版本而改变。然而,它的一些特性——主要是不变性——是有保证的。

当您使用“double-brace 初始化”时,您正在定义一个从指定类型派生的新匿名 class。所以 MySillyTest 扩展 HashSet 因为那是你指定的。注意double-brace初始化has problems;我不允许,也不鼓励别人用

两者之间的重要区别在于 Set.of(...) 导致的不变性。如果您需要一个可变集,那不是一个选择。但是,如果您可以使用不可变集,它会提供卓越的可读性和性能。

即使您需要可变集,也不要将 double-brace 初始化视为 Set.of(...) 的替代方法;只需以常规方式使用 HashSet

不知道,不关心

Set.of (and List.of, Map.of) 使用的具体 class 未记录。我们所知道的是对象 returned (a) 实现了接口,并且 (b) 是不可修改的。

这就是我们需要知道的全部内容。我们不应该关心幕后使用的具体具体 class。

作为未知的具体 class 为 of 方法的实施者提供了自由。

  • 那些程序员可以根据你的论点性质自由优化。例如,如果您传递枚举参数,高度优化的 EnumSet class 可能会在幕后使用。
  • 那些程序员可以在 Java 的版本之间自由更改他们的具体 class。例如,Java 17 实施可能 return 一个具体 class 而 Java 18 return 是另一个。

所以你应该永远不要依赖于of/copyOf方法所使用的特定具体class

你问过:

What is the real difference between those two?

在你的第一个中,我们知道了具体的 class。结果集是可修改的。

你的第二个,我们不知道具体的class,也不关心具体的class。结果集是不可修改的。

Code Concrete Class Modifiable
new HashSet<>() {{ add("a"); add("b"); add("c"); }} known modifiable
Set.of( "a", "b", "c" unknown unmodifiable

避免double-brace

正如其他人所说,一般最好避免double-brace初始化。

如果您想要紧凑 literals-style 初始化可修改集合的便利性,请结合使用 of 方法。您可以将现有集合传递给构造函数。

Set< String > set =
    new HashSet<>(
        Set.of( "a", "b", "c" )  // Pass a collection to constructor. 
    )
;