Java 9集合工厂的用法

Sor*_*ras 10 java collections java-9

List.of()或Collections.emptyList()List.of(...)或Collections.unmodifiableList()中给出的注释和答案的上下文中, 我提出了两条以下经验法则(也适用于SetMap工厂相应).

  1. 不要替换所有出现的事件

继续使用Collections.emptyList()以提高可读性,例如初始化懒惰的字段成员,例如:

class Bean {
  private List<Bean> beans = Collection.emptyList();
  public List<Bean> getBeans() {
    if (beans == Collections.EMPTY_LIST) { beans = new ArrayList<>(); }
    return beans;
  }
}
Run Code Online (Sandbox Code Playgroud)
  1. 使用新工厂作为方法参数构建器

在使用参数List.of()调用可执行文件时,使用新工厂和变体作为快速且较少类型的版本List.以下是我目前的替代模式:

Collections.emptyList()       --> List.of()
Collections.singletonList(a)  --> List.of(a)
Arrays.asList(a, ..., z)      --> List.of(a, ..., z)
Run Code Online (Sandbox Code Playgroud)

在虚构的用法中Collections.indexOfSubList,以下几行

Collections.indexOfSubList(Arrays.asList(1, 2, 3), Collections.emptyList());
Collections.indexOfSubList(Arrays.asList(1, 2, 3), Collections.singletonList(1));
Collections.indexOfSubList(Arrays.asList(1, 2, 3), Arrays.asList(1));
Collections.indexOfSubList(Arrays.asList(1, 2, 3), Arrays.asList(2, 3));
Collections.indexOfSubList(Arrays.asList(1, 2, 3), Arrays.asList(1, 2, 3));
Run Code Online (Sandbox Code Playgroud)

会读

Collections.indexOfSubList(List.of(1, 2, 3), List.of());
Collections.indexOfSubList(List.of(1, 2, 3), List.of(1));
Collections.indexOfSubList(List.of(1, 2, 3), List.of(1));
Collections.indexOfSubList(List.of(1, 2, 3), List.of(2, 3));
Collections.indexOfSubList(List.of(1, 2, 3), List.of(1, 2, 3));
Run Code Online (Sandbox Code Playgroud)

你(dis-)同意吗?

Stu*_*rks 11

通常,使用新工厂对于新代码是安全的,其中没有现有代码依赖于现有集合的行为.

有几个原因,新的集合工厂不是用于使用现有API初始化集合的代码的替代品.显然不变性是最突出的原因之一; 如果以后需要修改集合,那显然不可能是不可变的!但还有其他原因,其中一些非常微妙.

有关使用新API替换现有API的示例,请参阅JDK-8134373.审核主题在这里:Part1 Part2.

这是问题的概述.

数组包装与复制.有时您有一个数组,例如varargs参数,并且您希望将其作为列表进行处理.有时这Arrays.asList是最合适的东西,因为它只是一个包装器.相比之下,List.of创建一个副本,这可能是浪费.另一方面,调用者仍然具有包装数组的句柄并且可以修改它,这可能是一个问题,因此有时您需要支付复制它的费用,例如,如果您想保留对它的引用在实例变量中列出.

哈希集合迭代命令.new Set.ofMap.of结构随机化它们的迭代顺序.的迭代顺序HashSetHashMap是不确定的,但在实践中被证明是相对稳定的.代码可能会对迭代顺序产生无意的依赖.切换到新的集合工厂可能会将旧代码暴露给迭代顺序依赖项,从而浮现出潜在的错误.

禁止无效.新集合完全禁止空值,而常见的非并发集合(ArrayList,HashMap)允许它们.

序列化格式.新集合具有与旧集合不同的序列化格式.如果集合是序列化的,或者它存储在序列化的其他类中,则序列化输出将有所不同.这可能是也可能不是问题.但是,如果您希望与其他系统进行互操作,则可能会出现问题.特别是,如果将新集合的序列化形式传输到Java 8 JVM,则无法反序列化,因为Java 8上不存在新类.

严格的Mutator方法行为.新集合是不可变的,因此UnsupportedOperationException当调用mutator方法时它们会抛出.但是,有一些边缘情况,其中行为在所有集合中不一致.例如,

    Collections.singletonList("").addAll(Collections.emptyList())
Run Code Online (Sandbox Code Playgroud)

什么都不做,而

    List.of("").addAll(Collections.emptyList())
Run Code Online (Sandbox Code Playgroud)

将抛出UOE.通常,即使没有发生实际的突变,新集合和不可修改的包装器也一直严格地在对mutator方法的任何调用上抛出UOE.其他不可变集合(例如来自Collections.empty*和的集合)Collections.singleton*仅在发生实际变异时才会抛出UOE.

重复.新工厂SetMap工厂拒绝重复的元素和密钥.如果您使用常量列表初始化集合,这通常不是问题.实际上,如果一个常量列表有重复,那可能就是一个bug.这可能是一个问题,当允许调用者传入元素的集合或数组(例如,varags)时.如果调用者传入重复项,则现有API将默默地省略重复项,而新工厂将抛出IllegalArgumentException.这是一种可能会影响来电者的行为变化.


这些问题都不是致命问题,但在改进现有代码时,您应该注意行为差异.不幸的是,这意味着用新的收集工厂大规模替换现有的调用可能是不明智的.可能需要在每个站点进行一些检查,以评估行为变化的任何潜在影响.


Nic*_*lai 7

(IM)可变性

首先,重要的是要注意集合工厂返回不可变的变体.不幸的是,这不会在类型系统中显示,因此您必须手动/精神跟踪.这已经禁止了一些可能值得的替换,因此它必须在您的规则列表中变为0.:)

例如,创建稍后由其他代码修改的种子元素集合可能如下所示:

private final Set<String> commonLetters = initialCommonLetters()

private static Set<String> initialCommonLetters() {
    Set<String> letters = new HashSet<>();
    letters.add("a");
    letters.add("e");
    return letters;
}
Run Code Online (Sandbox Code Playgroud)

简单地编写会很好commonLetters = Set.of("a", "e");但是这可能会破坏其他代码,因为返回的集合是不可变的.

常量

(im)可变性讨论立即导致常数.这是介绍它们的主要原因!您需要静态初始化程序块来创建该COMMON_LETTERS常量的日子已经一去不复返了.因此,这将是我首先查看用例的地方.

更换

正如你所说,似乎没有理由开始更换来电Collections::empty...,Collections::singleton...Arrays::asList只是为了好玩.但是,一旦我开始在类中使用新方法,我会做的就是替换旧的变体,让代码依赖于更少的概念,使理解变得更容易.

偏爱

最后一个论点也可以适用of()于一般的变体.虽然Collections::empty...并且Collections::singleton...对他们的意图有些清楚,但我倾向于说,of无论你有多少个参数,总是使用,通过编写整体上使用较少概念的代码来抵消这种优势.

我认为没有理由继续使用Arrays::asList.

  • 只是包括它. (2认同)