在Java中,String[]::new和new String[]之间有区别吗?

huangapple go评论84阅读模式
英文:

Is there a difference between String[]::new and new String[] in java?

问题

我正在将一个ArrayList转换为数组,方法如下:

ArrayList<String> var1 = new ArrayList<>();
for (条件){
// 向var1添加项目
}

return var1.toArray(new String[]{})

我的集成开发环境建议我将最后一行更改为:

return var1.toArray(String[]::new)

这只是更好的惯例,还是在编译时或内存使用方面也有一些好处呢?

英文:

I am converting an ArrayList to an Array using

ArrayList&lt;String&gt; var1 = new ArrayList&lt;&gt;();
for(condition){
// Add items to var1
}

return var1.toArray(new String[]{})

My IDE is recommending i change the last line to

return var1.toArray(String[]::new)

Is this just better convention or is there also some benefits to compile-time or memory usage.

答案1

得分: 8

String[]::new 是一个函数。它不会创建一个新的字符串数组,而是定义了一个创建新字符串数组的函数。这就好比蛋糕和制作蛋糕的配方之间的区别。一个由面粉、糖霜等组成,另一个只是纸和文字。虽然完全不同,但又有关联,因为配方可以让你制作蛋糕(甚至可以制作任意数量的蛋糕!)

String[]::new 是以下代码的简写形式:

public String[] makeNewStringArray(int size) {
    return new String[size];
}

仅仅编写这段代码并不会创建任何字符串数组;调用这个函数每次都会创建一个字符串数组。

示例:

IntFunction<String[]> stringArrayMaker = String[]::new;
String[] size10 = stringArrayMaker.apply(10);

好的,那关于建议呢?

你所写的代码略微不太标准;new String[0] 更为常见。因此,我们来比较一下 new String[0]String[]::new

这个参数的唯一目的是让 ArrayList 知道你想要的类型。尽管 ArrayList 已经知道了(毕竟它是 ArrayList<String>!),但由于泛型擦除(一个相当复杂的主题),toArray 方法无法知道你想要的类型,因此你必须指定。

new String[0] 最终会创建一个长度为 0 的字符串数组,然后立即被丢弃。尽管这看起来有些浪费,但由于垃圾回收的机制,所谓的“快速垃圾”(快速创建并丢弃的对象,并且从未与其他线程共享)几乎是免费的。toArray(T[]) 方法的工作原理如下:如果传入的数组大小大于等于所需大小,就会填充并返回它。如果它太小,就会创建一个新的具有相同组件类型和正确大小的数组,将其填充,然后返回它。

你可能会认为 var1.toArray(new String[var1.size()]) 才是最高效的方式。但是 JMH(Java Microbenchmarking Harness)告诉我,实际上没有任何区别。这表明 Java 在处理短生命周期垃圾方面非常高效(当我说“短生命周期垃圾是免费的”时,我的意思是真的如此。几乎不可能察觉到它的成本)。

函数式的方法(传递 String[]::new)意味着这个函数将负责创建数组,而 toArray 本身只依赖于它所接收到的内容。这样也是可以的。它最终会在你的类文件中生成一个方法。

因此,实际上并不重要。我觉得这里 IDE 过于唠叨了。String[]::new 会生成一个新方法,仅仅在类空间方面的开销可能意味着它最终效率较低,尽管创建垃圾的成本更容易理解。

但是,我们现在在吹毛求疵。我们实际上在谈论纳秒级别的问题 - 你几乎察觉不到这些差异。

按照你认为最可读的方式来做(尽管我会避免 new String[]{} - 并没有什么问题,但它并不是很常见,与 new String[0] 相比没有任何好处 - 如果没有其他特殊情况,最好还是按照社区的做法)。

英文:

String[]::new is a function. It does not create a new string array, it defines a function that makes new string arrays. It's like the difference between a cake, and a recipe to make a cake. One consists of flour and icing and all that. The other one is just words and paper. Completely different things, but related, in that the recipe lets you make cake (any number of them, even!)

String[]::new is shorthand* for:

public String[] makeNewStringArray(int size) {
    return new String[size];
}

Merely writing that doesn't make any string arrays at all; calling that function would make one string array each time it is invoked.

Example:

IntFunction&lt;String[]&gt; stringArrayMaker = String[]::new;
String[] size10 = stringArrayMaker.apply(10);

Okay, so what about the recommendation?

What you've written is slightly non-standard; new String[0] is more standard. So let's compare new String[0] versus String[]::new.

The only point of this parameter is to allow the arraylist to know what type you want. You'd think the arraylist already knows (it is an ArrayList&lt;String&gt; after all!) but due to generics erasure (a rather complex topic), there is no way for the code of toArray to know what you want, therefore, you must specify.

new String[0] ends up making a 0-length string array which is then immediately discarded. This feels wasteful, but garbage collection being what it is, 'quick garbage' (objects made and discarded very quickly and never shared with other threads) is almost entirely free. This method (toArray(T[])) works as follows: If the incoming array is as large or larger than is needed, it is filled, and returned. If it is too small, a new array is created with the same component type of the right size, that one is filled, and that one is returned.

You'd think, therefore, that var1.toArray(new String[var1.size()]) is the most efficient. But JMH tells me there is no difference at all. Which goes to show how incredibly efficient java is at dealing with short lived garbage (when I say 'short lived garbage is free', I mean it. It's almost impossible to witness the cost of it).

The functional approach (where you pass String[]::new) means that this function will take care of making the array, and toArray itself just relies on what it gets. This.. also works fine. It ends up making a method in your class file someplace.

So, it really doesn't matter. I find the IDE being overly preachy here. String[]::new makes a new method, and the overhead in class space alone probably means it ends up being less efficient, even though the creation of the garbage is more easily understandable inefficiency.

But, we're really nitpicking here. Literally talking about nanoseconds - you're never going to notice.

Do whatever you find is most readable (though I would steer clear of new String[]{} - nothing wrong with it perse, but it's not very common and it has no benefits over new String[0] - might as well do what the community does if it doesn't otherwise matter).

*) This is slightly oversimplified, but not by much.

答案2

得分: -6

"String[]::new" 是一个方法引用。它基本上和 "new String[]" 是一样的。更多细节请参阅方法引用

英文:

"String[]::new" is a method reference. Its basically the same as "new String[]". See Method References for more detail.

huangapple
  • 本文由 发表于 2020年7月23日 21:09:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/63055070.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定