This is inconsistency in the implementation of TreeSet<E> , bordering on an error. The code will ignore the custom comparator when the number of elements in the collection that you pass to removeAll is greater than or equal to the number of elements in the set.
The inconsistency is caused by a small optimization: if you look at the implementation of removeAll , which is inherited from AbstractSet , the optimization is performed as follows:
public boolean removeAll(Collection<?> c) { boolean modified = false; if (size() > c.size()) { for (Iterator<?> i = c.iterator(); i.hasNext(); ) modified |= remove(i.next()); } else { for (Iterator<?> i = iterator(); i.hasNext(); ) { if (c.contains(i.next())) { i.remove(); modified = true; } } } return modified; }
you can see that the behavior is different when c has fewer elements than this set (upper branch), and when it has the same or more elements (lower branch).
The upper branch uses the comparator associated with this set, while the lower branch uses equals to compare c.contains(i.next()) - all in one method!
You can demonstrate this behavior by adding a few additional elements to the original set of trees:
s.addAll(Arrays.asList("x", "z", "a", "b"));
Now the output for both test cases becomes identical, because remove(i.next()) uses a set comparator.
dasblinkenlight
source share