Pass zero-sized array, save allocation?

2019-06-17 05:44发布

问题:

In this code sample from page 114 of The Well-Grounded Java Developer, the last line:

Update[] updates = lu.toArray(new Update[0]);

contains the note: Pass zero-sized array, save allocation

List<Update> lu = new ArrayList<Update>();
String text = "";
final Update.Builder ub = new Update.Builder();
final Author a = new Author("Tallulah");

for (int i=0; i<256; i++) {
  text = text + "X";
  long now = System.currentTimeMillis();
  lu.add(ub.author(a).updateText(text).createTime(now).build());
  try {
    Thread.sleep(1);
  } catch (InterruptedException e) {
  }
}

Collections.shuffle(lu);
Update[] updates = lu.toArray(new Update[0]);

What allocation is this saving, exactly?

The javadoc for List#toArray(T[] a) mentions:

If the list fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this list.

Which is what I remembered: if the array you pass to toArray(T[] a) can't fit everything in the list, a new array is allocated. Plainly, there are 256 elements in the list, which cannot fit in an array of size 0, therefore a new array must be allocated inside the method, right?

So is that note incorrect? Or is there something else it means?

回答1:

Plainly, there are 256 elements in the list, which cannot fit in an array of size 0, therefore a new array must be allocated inside the method, right?

yes.


You can use

 private static final Update NO_UPDATES = { }

 lu.toArray(NO_UPDATES);

however this will should only help if you expect the list to be typically 0 length.

Generally, I would the same approach as fge

 lu.toArray(new Update[lu.size()]);

In your specific case you know the size in advance so you can do

Update[] updates = new Update[256];
String text = "";
final Update.Builder ub = new Update.Builder();
final Author a = new Author("Tallulah");

long now = System.currentTimeMillis();
for (int i=0; i<updates.length; i++) 
  updates[i] = ub.author(a).updateText(text += 'X').createTime(now++).build();

Collections.shuffle(Arrays.asList(updates));


回答2:

Going off of @Andreas comment on the question, I think it is a typo, and should say:

Pass zero-sized array, safe allocation.

Because if you passed nothing to the method, you'll end up calling the List#toArray() no-argument overload!

This would return an Object[] (though it would contain nothing but Update instances) and would require changing the type of the updates variable, so the last line would become:

Object[] updates = lu.toArray();

And then every time you wanted to iterate over and use the elements in that array, you'd have to cast them to Update.

Supplying the array calls the List#toArray(T[] a) method, which returns a <T> T[]. This array is reified to know it is an array of Update instances.

So supplying an empty array of Updates results in an Update[] coming back from the toArray call, not an Object[]. This is a much more type-safe allocation! The word "save" in the note must be a typo!

...this consumed way too much mental effort. Will post link to this in the book's forums so they can correct it.



回答3:

It saves allocation, comparing to toArray(new Update[255]) or toArray(new Update[1000])