Java 9附带convenience factory methods用于创建不可变列表。最后,列表创建非常简单:
List<String> list = List.of("foo", "bar");
但是这个方法有12个重载版本,11个有0到10个元素,还有一个有var args。
static <E> List<E> of(E... elements)
Set
和Map
的情况相同。
由于存在var args方法,有多少11个方法有什么意义?
我认为var-args会创建一个数组,因此其他11种方法可以跳过创建额外的对象,在大多数情况下会有0-10个元素。还有其他原因吗?
答案 0 :(得分:25)
来自JEP docs本身 -
说明 -
这些将包括varargs重载,因此没有固定限制 关于集合大小。但是,这样创建的集合实例 可以调整为更小的尺寸。特殊情况API(固定参数 过载)将提供多达十个元素。 虽然这个 在API中引入了一些混乱,它避免了数组分配, 初始化和由此引起的垃圾收集开销 varargs调用。值得注意的是,无论是否调用fixed-arg或varargs重载,调用站点的源代码都是相同的。
编辑 - 增加动力,正如@CKing在评论中已提到的那样:
非目标 -
支持高性能,可扩展的集合并非目标 任意数量的元素。 重点是小型馆藏。
动机 -
创建一个小的,不可修改的集合(比如一个集合)包括构造它,将它存储在局部变量中,并在其上多次调用add(),然后将其包装。
Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a", "b", "c")));
通过组合流工厂方法和收集器,Java 8 Stream API可用于构建小型集合。
// Java 8
Set<String> set1 = Collections.unmodifiableSet(Stream.of("a", "b", "c").collect(Collectors.toSet()));
通过提供用于创建小型集合实例的库API,可以获得收集文字的大部分好处,与更改语言相比,成本和风险显着降低。例如,创建小型Set实例的代码可能如下所示:
// Java 9
Set set2 = Set.of("a", "b", "c");
答案 1 :(得分:10)
如您所料,这是一项性能提升。 Vararg方法在“引擎盖下”创建一个数组,并且拥有直接采用1-10个参数的方法可以避免这种冗余数组的创建。
答案 2 :(得分:6)
您可能会发现Josh Bloch的 Effective Java (第2版)第42项的以下段落具有启发性:
每次调用varargs方法都会导致数组分配和初始化。如果你根据经验确定你不能承担这笔费用,但你需要varargs的灵活性,有一种模式可以让你吃蛋糕并吃掉它。假设您已确定95%的方法调用具有三个或更少的参数。然后声明方法的五个重载,一个用零到三个普通参数,以及当参数个数超过三个时使用的单个varargs方法[...]
答案 3 :(得分:3)
你也可以反过来看看它。由于 varargs 方法可以接受数组,因此这种方法可以作为将数组转换为List
的替代方法。
String []strArr = new String[]{"1","2"};
List<String> list = List.of(strArr);
此方法的替代方法是使用Arrays.asList
,但在这种情况下对List
所做的任何更改都会反映在数组中,List.of
不是这种情况。因此,当您不希望List.of
和数组同步时,您可以使用List
。
注意规范中给出的理由对我来说似乎是一种微观优化。 (这已经由API的所有者在another回答)
的评论中得到了证实答案 4 :(得分:2)
此模式用于优化接受varargs参数的方法。
如果您能够确定只使用其中几个的大部分时间,您可能希望使用最常用的参数量来定义方法过载:
public void foo(int num1);
public void foo(int num1, int num2);
public void foo(int num1, int num2, int num3);
public void foo(int... nums);
这将帮助您在调用varargs方法时避免创建数组。用于性能优化的模式:
List<String> list = List.of("foo", "bar");
// Delegates call here
static <E> List<E> of(E e1, E e2) {
return new ImmutableCollections.List2<>(e1, e2); // Constructor with 2 parameters, varargs avoided!
}
这背后更有趣的是从3个参数开始我们再次委托给varargs构造函数:
static <E> List<E> of(E e1, E e2, E e3) {
return new ImmutableCollections.ListN<>(e1, e2, e3); // varargs constructor
}
现在看起来很奇怪,但正如我猜测的那样 - 这是为未来的改进而保留的,作为一种选择,所有构造函数的潜在重载List3(3 params), List7(7 params)...
等等。
答案 5 :(得分:-1)
根据Java doc:便利工厂方法返回的集合比其可变等价物更节省空间。
在Java 9之前:
Set<String> set = new HashSet<>(3); // 3 buckets
set.add("Hello");
set.add("World");
set = Collections.unmodifiableSet(set);
在Set
的上述实现中,有6个对象正在创建:不可修改的包装器; HashSet
,其中包含HashMap
;桶表(数组);和两个Node实例(每个元素一个)。如果VM每个对象占用12个字节,则有72个字节作为开销消耗,加上28 * 2 = 56个字节用于2个元素。与存储在集合中的数据相比,大量消耗由开销消耗。但是在Java 9中,这种开销非常小。
在Java 9之后:
Set<String> set = Set.of("Hello", "World");
在Set
的上述实现中,只创建了一个对象,由于开销最小,这将占用数据的空间非常小。