ConcurrentSkipListSet using Comparator will not add new unique values

dorony :

I want a concurrent set of string values sorted by length longest -> shortest.

This is my code (JAVA 8):

private ConcurrentSkipListSet<String> sortedSourceTypeNames = new ConcurrentSkipListSet<>(Comparator.comparing(String::length).reversed());

Here is the java 8 documentation:

    /**
     * Constructs a new, empty set that orders its elements according to
     * the specified comparator.
     *
     * @param comparator the comparator that will be used to order this set.
     *        If {@code null}, the {@linkplain Comparable natural
     *        ordering} of the elements will be used.
     */
    public ConcurrentSkipListSet(Comparator<? super E> comparator) {
        m = new ConcurrentSkipListMap<E,Object>(comparator);
    }

Now here is the weird thing:

  1. add new value "some_str" -> ok
  2. add new value "some_els" -> not added
  3. add new value "some" -> ok

Debugging this phenomenon I saw that the ConcurrentSkipListSet is rejecting new unique strings that have the same length of an already existing string in the set.

And I was like Waaaattt?!?!?

This is an unexpected behavior which is not mentioned in any documentation.

Is this a bug in the JAVA ConcurrentSkipListSet implementation? or it's something I did?

Edit:

Thank u all for the quick response!

I just want to point out that this behavior is documented in the JAVA SortedSet interface (but not in the ConcurrentSkipListSet):

* <p>Note that the ordering maintained by a sorted set (whether or not an
 * explicit comparator is provided) must be <i>consistent with equals</i> if
 * the sorted set is to correctly implement the <tt>Set</tt> interface.  (See
 * the <tt>Comparable</tt> interface or <tt>Comparator</tt> interface for a
 * precise definition of <i>consistent with equals</i>.)  This is so because
 * the <tt>Set</tt> interface is defined in terms of the <tt>equals</tt>
 * operation, but a sorted set performs all element comparisons using its
 * <tt>compareTo</tt> (or <tt>compare</tt>) method, so two elements that are
 * deemed equal by this method are, from the standpoint of the sorted set,
 * equal.  The behavior of a sorted set <i>is</i> well-defined even if its
 * ordering is inconsistent with equals; it just fails to obey the general
 * contract of the <tt>Set</tt> interface.
Peter Lawrey :

The comparator you have provider returns that strings of the same length are equal, thus duplicates which are ignored.

The default use of ConcurrentSkipListSet is like

Set<String> set = new ConcurrentSkipListSet<>(
        Comparator.comparing(s -> s));

or

Set<String> set = new ConcurrentSkipListSet<>(
        Comparator.naturalOrder());

when you set the Comparator, you replace the default comparator and it doesn't default back to the default behaviour if your comparator states two objects are equal.

One way around this is to sort by length and then by content on equal length.

Set<String> set = new ConcurrentSkipListSet<>(
        Comparator.comparing(String::length).reversed()
        .thenComparing(s -> s));

set.add("aa");
set.add("bb");
set.add("aaa");
set.add("ccc");
System.out.println(set);

prints

[aaa, ccc, aa, bb]

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=461228&siteId=1