Is it recommended to cache Collections.unmodifiableList() result in a field?

Viewed 482

Which one is the recommended approach:

private final List<Object> list = new ArrayList<>();

public List<Object> getListView() {
    return Collections.unmodifiableList(list);
}

or

private final List<Object> list = new ArrayList<>();
private final List<Object> listView = Collections.unmodifiableList(list);

public List<Object> getListView() {
    return listView;
}

The latter saves on object creation, but is it worth the effort?

4 Answers

Creating an unmodifiableList is an O(1) operation (essentially, it instantiates a java.util.Collections$UnmodifiableList and assings your list to a datamember).

Unless you have a very convincing benchmark of your special-case that shows the contrary, it probably just isn't worth the hassle of caching it.

The second approach definitely saves you allocations and deallocations. However, the frequency of such allocations is not going to be critical to justify storing listView object. Consider a typical use:

for (Object obj : getListView()) {
    ... // Do something with each object
}

A single call to getListView() is followed by N iterations on its content, making the share of time spent creating an unmodifiable view too small to justify the effort.

In situations where list and listView are non-final there would be ab additional concern of maintaining the consistency between the two objects. I would definitely caution against caching in a class where list and listView could get "out of sync".

Approach 1 is recommended as a lazy load scenario and can save you a few resources. Also creating an unmodifiableList is not an expensive operation.

Most of the time, the difference is hardly measurable, so I'd keep it simple. However, there are cases, when there may be a substantial difference:

 Collections.unmodifiableList(list).equals(Collections.unmodifiableList(list))

is an O(n) operation, as the shortcut using == does not work. Such a comparison may be needed, e.g., when you use the list as a cache key.

Despite that, I'd suggest to keep it simple, and only optimize, when the application needs it and the profiler shows that it may help.

Consider using Guava's ImmutableList, if you can.

Related