当前位置: 首页 > 知识库问答 >
问题:

Java9, Set.of()和Map.of()varargs重载[重复]

陆洛城
2023-03-14

我正在研究不可变集合的工厂方法。我看到集合了。of()方法有10个varargs重载(与Map.of()相同)。我真的不明白为什么有这么多。最后,函数ImmutableCollections。塞恩

在留档里我发现了这个:

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

杂乱无章的东西真的值得性能提升吗?如果是,理想情况下,这会为任何N元素创建一个单独的方法吗?


共有3个答案

凌宏大
2023-03-14

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

所以你有:

  1. 一方面,这些不可变类可能会被应用程序使用,在应用程序中,每一位都会计数(每一纳秒都会浪费在分配/释放上)

如果你实现你自己的东西,我不会那么在意(但是要小心varargs参数),除非你真的需要担心那些额外的比特(和额外的性能等等)。

蓝昊天
2023-03-14

其中的某些方面可能是未来打样的一种形式。

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

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

我们可以说瓦拉格一家足够好。。。除了varargs强制分配一个对象数组,这虽然相当便宜,但实际上会影响性能。例如,请参阅这个微基准,它显示了在切换到varargs表单时,无操作日志记录(即日志级别低于可记录级别)的吞吐量损失为50%。

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

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

这不是二进制兼容的它是源兼容的,但不是二进制兼容的为了保持二进制兼容,我们需要保留以前的签名,例如:

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创建成本的程度,这可能是大约10个参数(至少基于Log4J2在将vararg添加到版本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方法,每个人都获得了胜利。。。无需重新编译

巢睿
2023-03-14

无论如何,在调用该方法的那一刻,这可能会改变。例如,它可以创建一个只包含三个元素的集合,4等等。

也不是所有元素都委托给SetN——包含零、一和两个元素的元素具有实际的不可变集合类。Set0不可变集合。Set1不可变集合。Set2

或者你可以阅读关于这个的实际问题。。。这里请阅读斯图尔特·马克斯(Stuart Marks)在这个问题上的评论,因为他是这些藏品的创造者。

 类似资料:
  • 在Java 9中,在列表界面下,有一个新的()方法,根据Java文档: 名单。of()静态工厂方法提供了一种创建不可变列表的方便方法。

  • 本文向大家介绍Java Varargs中的方法重载和歧义,包括了Java Varargs中的方法重载和歧义的使用技巧和注意事项,需要的朋友参考一下 在Java中使用变量参数时存在歧义。发生这种情况是因为两种方法绝对可以有效地被数据值调用。因此,编译器不知道该调用哪种方法。 示例 输出结果 名为Demo的类定义了一个名为“ my_fun”的函数,该函数采用可变数量的浮点值。使用“ for”循环将这些

  • 问题内容: 这将无法编译: 这将编译并工作: 第一个和第二个示例非常相似。首先使用varargs,第二个不使用。为什么一个有效,第二个无效。7是原始的,因此在两种情况下都应调用第二种方法。这是正常行为吗? 我找到了: 错误报告 堆栈溢出 问题答案: 这是正在发生的情况的高层非正式摘要。 首先,varargs语法实际上只是传递数组的语法加糖。所以实际上是要传递一个…数组。 但是数组是什么?这里有两个

  • 问题内容: 以下代码无法编译。 发出编译时错误。 对test的引用是模棱两可的,varargspkg.Main中的方法test(int …)和varargspkg.Main中的方法test(float …) 这似乎很明显,因为方法调用中的参数值可以提升为 如果任何一个或两个参数都带有或作为后缀,则会进行编译。 但是,如果我们用相应的包装器类型表示方法签名中的接收参数,如下所示 那么对该方法的调用不

  • 我有两个重载的方法,分别是varargs int和long。当我运行一个传递整数的测试时,它似乎更喜欢varargs long方法。然而,如果我使这些方法是静态的并使用整数运行,它似乎更喜欢varargs int方法。这是怎么回事? 产出: 内长varargs 1 内部静态int varargs 1

  • J.Bloch在《Effective Java》中提到,将varargs方法与基元类型一起使用是不安全的。简而言之,的返回类型为,这听起来很合理。现在我试图自己复制这种行为,但做不到: 我的问题是,为什么类型被推导为,而不是像他所说的那样被推导为?这是否意味着,在Java8中,关于varargs的问题不再相关了,如果我们不太关心性能,我们可以在任何地方安全地使用它们。