Java 9,Set.of()和Map.of()varargs重载

时间:2017-08-02 07:28:38

标签: java collections java-9

我正在研究Immutable收藏品的工厂方法。 我看到Set.of()方法有10个varargs重载(Map.of()相同)。我真的无法理解为什么会这么多。最后,无论如何都会调用函数ImmutableCollections.SetN<>(elements)

在文档中我发现了这个:

  

虽然这会在API中引入一些混乱,但它避免了varargs调用引起的数组分配,初始化和垃圾收集开销。

杂乱的确是否值得获得性能提升?如果是,理想情况下是否会为任何N元素创建单独的方法?

3 个答案:

答案 0 :(得分:11)

目前无论如何调用该方法 - 这可能会改变。例如,它可能会创建一个只有三个元素的Set,依此类推。

并非所有委托给SetN - 具有零,一和二元素的元素具有ImmutableCollections.Set0ImmutableCollections.Set1和{的实际类别{1}}

您可以阅读有关此问题的实际问题... here在该问题中阅读ImmutableCollections.Set2中的评论 - 因为他是创建这些收藏集的人。

答案 1 :(得分:10)

这方面的某些方面可能是未来证明的一种形式。

如果你发展了一个API,你需要注意方法签名将如何改变,所以如果我们有

public class API {
  public static final <T> Set<T> of(T... elements) { ... }
}

我们可以说varargs足够好了...除了varargs 强制分配一个对象数组,这虽然相当便宜,但确实影响了性能。例如,参见this microbenchmark,当切换到varargs表单时,显示无操作日志记录的吞吐量损失50%(即日志级别低于loggable)。

好的,我们做了一些分析并说最常见的情况是单身,所以我们决定重构...

public class API {
  public static final <T> Set<T> of(T first) { ... }
  public static final <T> Set<T> of(T first, T... others) { ... }
}

Ooops ...它不是二进制兼容的...它的源兼容,但不兼容二进制...保留二进制兼容性,我们需要保留以前的签名,例如

public class API {
  public static final <T> Set<T> of(T first) { ... }
  @Deprecated public static final <T> Set<T> of(T... elements) { ... }
  public static final <T> Set<T> of(T first, T... others) { ... }
}

呃...... IDE代码完成现在一团糟......再加上如何创建一组数组? (如果我使用列表可能更相关)API.of(new Object[0])是不明确的......如果我们没有在开始时添加vararg ...

所以我认为他们所做的是添加足够的显式args以达到额外堆栈大小满足vararg创建成本的程度,这可能是大约10个参数(至少基于Log4J2在添加varargs时所做的测量)到版本2 API)...但是你这样做是为了证明基于证据的未来......

换句话说,我们可以欺骗所有我们没有证据需要专门实施的案例,只是落入vararg变体:

public class API {
  private static final <T> Set<T> internalOf(T... elements) { ... }
  public static final <T> Set<T> of(T first) { return internalOf(first); }
  public static final <T> Set<T> of(T first, T second) { return internalOf(first, second); }
  ...
  public static final <T> Set<T> of(T t1, T t2, T t3, T t4, T t5, T... rest) { ... }
}

然后我们可以分析和查看现实世界的使用模式,如果我们看到重要的使用情况,直到4个arg形式和基准显示有合理的性能增益,那么在那时,在幕后,我们改变了方法impl,每个人都获胜......不需要重新编译

答案 2 :(得分:2)

我想这取决于您正在使用的API的范围。在谈论那些不可变类时,你正在谈论作为jdk的一部分包含的东西;所以范围很广。

所以你有:

  1. 一方面,这些不可变类可能被应用程序使用,其中每个位都计数(并且在分配/释放中浪费了每纳秒)。
  2. 另一方面,没有这些需求的应用程序不会受到负面影响
  3. 唯一的“消极”方面是该API的实现者会有更多的杂乱处理,因此它会影响可维护性(但在这种情况下不是很重要)。
  4. 如果你正在实现自己的东西,我不会那么关心(但要对varargs参数保持谨慎),除非你真的需要担心那些额外的位(和额外的性能etcetc)。