What’s the difference

Those two collections are all sorted and very useful when you need to maintain some order in dynamically filled data. However, when concurrency takes place there is a gotcha. We have a choice: use manual synchronization or use ConcurrentSkipListSet from java.concurrent package.

My case

I have a task that gets episodes from a server in a bulk operation. For example, when there is an 800 episodes total it makes 8 asynchronous requests 100 episodes each. Resulting list of episodes needs to be sorted and displayed on the client side.

Code for manual synchronizations looks like this:

final Set<Video> allEpisodes = new TreeSet<>(sortByEpisode);

final Video[] sortedEpisodes;

synchronized (allEpisodes) {
	Collections.addAll(allEpisodes, notFakeVideos);
	sortedEpisodes = allEpisodes.toArray(Video.EMPTY_ARRAY);
}
//use of sortedEpisodes

which is somewhat verbose.

In the opposite, code with concurrent collection looks very clean.:

final Set<Video> allEpisodes = new ConcurrentSkipListSet<>(sortByEpisode);

Collections.addAll(allEpisodes, notFakeVideos);

final Video[] sortedEpisodes = allEpisodes.toArray(Video.EMPTY_ARRAY);

Almost none of the concurrent stuff is visible to the programmer which is a good thing for maintainability and error-proneness.

Benchmark?

I’m tested two solutions using Android emulator API 30. Here are results:

solution avg ns min ns max ns # of experiments sum ns
concurrent 114960 6543 4109831 620 71275591
synchronized 44295 7035 1525028 620 27463261

For my use case manual synchronization is almost twice as fast as using concurrent collection.

But which is better?

Winner: TreeSet+synchronized! However, in broad case the answer is: “It depends”. My case consist of only 10 invocations of adding elements and one toArray() call. It is a tiny number of concurrent events. That’s why concurrent collection overhead makes a visible difference for performance.