1

I am converting an ArrayList to an Array using

ArrayList<String> var1 = new ArrayList<>();
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.

Parzavil
  • 388
  • 3
  • 13
  • `String[]::new` references the constructor of the type `String[]` and `ArrayList` has an overload of `toArray` with the signature `T[] toArray(IntFunction function)` which in turn the list can use to directly create an array with the correct size. – Ackdari Jul 23 '20 at 13:18

2 Answers2

8

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<String[]> 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<String> 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.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • 1
    Just to note: The default implementation of `toArray(IntFunction)` is simply `return toArray(generator.apply(0))`. So it's not much different from `toArray(new XXX[0])` except it allows the `Collection` implementation to decide to optimize the creation of the array, if such an optimization is possible. (none of the implementations appear to override the method except when the implementation is a wrapper and the method simply delegates) – Slaw Jul 23 '20 at 13:23
  • There is no difference between using `new String[0]` and `String[]::new` - as both instantiate an empty string array which is immediately replaced if `var1.size() > 0` - unless overridden as suggested by @Slaw. Surprisingly - and it did seem true when testing on my machine - discarding the empty String[0] is quicker than instantiating `new String[var1.size() ]` immediately. See full discussion [https://stackoverflow.com/questions/4042434/converting-arrayliststring-to-string-in-java] – DuncG Jul 24 '20 at 11:17
-6

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

Cerus
  • 141
  • 1
  • 11