Is there any memory/performance benefit of using the first method over the second?
First
public List<Integer> getList1(List<Integer> data) {
List<Integer> list = new ArrayList<Integer>();
for (Integer element: data) {
if (element % 2 == 0) {
list.add(element);
}
}
return list.isEmpty() ? Collections.<Integer>emptyList() : list;
}
Second
public List<Integer> getList2(List<Integer> data) {
List<Integer> list = new ArrayList<Integer>();
for (Integer element: data) {
if (element % 2 == 0) {
list.add(element);
}
}
return list;
}
The degree to which any of this matters depends on how often getList1 is called such that it returns an empty list, of course. But:
The JavaDoc for Collections#emptyList offers insight here:
Implementation note: Implementations of this method need not create a separate List object for each call. Using this method is likely to have comparable cost to using the like-named field. (Unlike this method, the field does not provide type safety.)
So yes, you (may) end up with fewer objects in memory if you use emptyList rather than returning a new empty list each time, because Collections#emptyList is free to return the same List<Integer> every time you call it (since it's immutable).
You may get an unrelated performance improvement as well (at least with Oracle's JVM). In some situations, and I'm not au fait with the details at all, but in some situations Oracle's JVM can initially allocate objects assigned to local variables on the stack, only transferring them to the heap if a reference to them survives the termination of the function call. Stack-based allocation is really fast and doesn't, of course, suffer from fragmentation. So in the empty list scenario, you not only avoid cluttering memory with empty lists, but you get a speed benefit as well (the JVM doesn't have to copy the empty list you created off the stack into the heap). But again, I don't know whether that JVM optimization necessarily applies to your code.
If the memory optimization is really important to your code and you don't want to rely on the stack-allocation thing, with a slight sacrifice to code clarity and a tiny runtime cost, you can avoid allocating a list at all if you don't need one:
public List<Integer> getList1(List<Integer> data) {
List<Integer> list = null;
for (Integer element: data) {
if (element % 2 == 0) {
if (list == null ) {
list = new ArrayList<Integer>();
}
list.add(element);
}
}
return list == null ? Collections.<Integer>emptyList() : list;
}
Note, though, that there's an important semantic difference. In your first code block, getList1 is inconsistent: It might return a list the calling code can modify (if it isn't empty), or it might return an immutable list (if it was empty). getList1 should probably always return a mutable or immutable list, not one sometimes and another other times. If it can be immutable, you should probably use Collections#unmodifiableList to get an immutable version of your non-empty list, and return that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With