使用Comparator的ConcurrentSkipListSet不会添加新的唯一值

dor*_*ony 6 java java.util.concurrent java-8

我想要并发的一组字符串值,按长度最长->最短排序。

这是我的代码(JAVA 8):

private ConcurrentSkipListSet<String> sortedSourceTypeNames = new ConcurrentSkipListSet<>(Comparator.comparing(String::length).reversed());
Run Code Online (Sandbox Code Playgroud)

这是Java 8文档:

    /**
     * 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);
    }
Run Code Online (Sandbox Code Playgroud)

现在这是奇怪的事情:

  1. 添加新值“ some_str”->确定
  2. 添加新值“ some_els”-> 未添加
  3. 添加新值“ some”->确定

调试此现象后,我看到ConcurrentSkipListSet拒绝了新的唯一字符串,这些字符串的长度与集合中现有字符串的长度相同。

我就像瓦阿特(Waaaattt)?!?!?

这是意外的行为,任何文档中都未提及。

这是JAVA ConcurrentSkipListSet实现中的错误吗?还是我做了什么?

编辑:

谢谢大家的快速回复!

我只想指出,此行为记录在JAVA SortedSet接口中(但未在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.
Run Code Online (Sandbox Code Playgroud)

Pet*_*rey 6

您拥有提供程序的比较器返回相同长度的字符串相等,因此忽略重复项。

默认使用ConcurrentSkipListSet是像

Set<String> set = new ConcurrentSkipListSet<>(
        Comparator.comparing(s -> s));
Run Code Online (Sandbox Code Playgroud)

或者

Set<String> set = new ConcurrentSkipListSet<>(
        Comparator.naturalOrder());
Run Code Online (Sandbox Code Playgroud)

当您设置 Comparator 时,您将替换默认比较器,并且如果您的比较器声明两个对象相等,则它不会默认恢复为默认行为。

解决此问题的一种方法是按长度排序,然后按等长的内容排序。

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);
Run Code Online (Sandbox Code Playgroud)

印刷

[aaa, ccc, aa, bb]
Run Code Online (Sandbox Code Playgroud)

  • 或者`Comparator.comparingInt(String::length).reversed() .thenComparing(Comparator.naturalOrder())`,这可能会更有效一些。 (2认同)
  • 请记住,`.thenComparing(s-&gt;s)` 只是 `.thenComparing(Comparator.comparing(s-&gt;s))` 的简写,所以你将单态的 `s-&gt;s` 函数与`Comparator.comparing(Function)` 返回的多态委托比较器(在使用 `Comparator.comparing(Function) .thenComparing(Function)` 时尤其如此)。两个变体,`.thenComparing(naturalOrder())` 和 `.thenComparing(s-&gt;s)` 都以调用 `Comparable.compareTo` 的多态比较器结束,但后者也将在每个元素之前评估提供的标识函数调用`compareTo`。 (2认同)