Java ConcurrentSkipSet与瞬时无限循环的比较

Java ConcurrentSkipSet与瞬时无限循环的比较,java,compare,comparator,java.time.instant,Java,Compare,Comparator,Java.time.instant,我正在使用比较器创建一个java.util.concurrent.ConcurrentSkipListSet。我的比较器比较对象中的即时字段以确定顺序。这是比较标准: public int compare(myObj first, myObj second) { if (first == null || first.getTime() == null) { return 1; } if (second == null ||

我正在使用比较器创建一个
java.util.concurrent.ConcurrentSkipListSet
。我的比较器比较对象中的即时字段以确定顺序。这是比较标准:

public int compare(myObj first, myObj second) {
        if (first == null || first.getTime() == null) {
            return 1; 
        }
        if (second == null || second.getTime() == null) {
            return -1;
        }
        
        if ( first.getTime().isBefore(second.getTime()) {
            return -1;
        }
        else { 
            return 1;
        }
    }
当我使用上面的比较器5/10次时,它被卡在一个无限循环中。第一个总是在第二个之后,所以程序总是到达else并返回1,然而在一些运行中它只是循环,它到达else但只是一直运行比较器。。。在调试模式下运行以及添加额外日志记录时,我可以看到这一点。为了澄清这两个对象在卡住时是不一样的,所以问题不是试图添加重复的。 当我像这样从isBefore切换到isAfter时,我不理解这种行为,它每次都按预期运行。我的问题是为什么会发生这种情况

    public int compare(myObj first, myObj second) {
        if (first == null || first.getTime() == null) {
            return 1; 
        }
        if (second == null || second.getTime() == null) {
            return -1;
        }
        
        if ( first.getTime().isAfter(second.getTime()) {
            return 1;
        }
        else { 
            return -1;
        }
    }
添加到列表实现中(为简单起见简化)

我的并发跳过集逻辑非常简单,我用上面的比较器创建了一个集,然后myObj instant被启动到now()+一个预先确定的分钟数(硬编码,这样计算就不会失败),然后我的obj被添加到concurrentskipset

private ConcurrentSkipListSet<MyObj> skipSetImpl= new ConcurrentSkipListSet<>(new MyObj.MyobjComp()); 

// added in a method, using Java Instant
foo.setTime( now.plus(60, ChronoUnit.SECONDS) ); 

this.skipSetImpl.add(foo); // add to concurrentSkipSet
private ConcurrentSkipListSet skipSetImpl=new ConcurrentSkipListSet(new MyObj.MyobjComp());
//添加到方法中,使用Java Instant
foo.setTime(现在加上(60秒);
this.skipSetImpl.add(foo);//添加到concurrentSkipSet

您没有履行Comparator.compare()的合同。如果没有您的
ConcurrentSkipSet
代码,我们无法判断这是否是您所问问题的根源,但很可能是

如果两个
MyObj
都是
null
,一个是
null
,另一个是
null
瞬间,或者两者都是
null
瞬间,则比较器无法确定它们的顺序。如果跳过集调用
compare(a,b)
a
应该排在最后,但如果下一个调用
compare(b,a)
,则突然
b
应该排在最后。这会导致它循环吗?在任何情况下,比较器的不一致性都不是一个好特性,而且它肯定会导致使用
比较器的其他类出现问题

compare
方法的文档说明:

实施者必须确保所有
x
y
sgn(比较(x,y))==-sgn(比较(y,x))
。(这意味着compare(x,y)必须在且仅当compare(y,x)时引发异常) 抛出异常。)

这就是你要打破的部分。您的跳过集应该能够依靠您遵守合同

你说:

它们不能与我的逻辑和程序的工作方式相同 永远不应该平等

如果是这样,您需要为任意两个
MyObj
确定一个订单。一种解决方案是不允许空值(如果出现
null
则抛出异常)。另一种方法是确定空值相等

定义将null放在最后的比较器的最简洁方法是通过
comparator.nullsLast()
comparator.comparating()
(自Java 8以来):

Comparator comp=Comparator.nullsLast(Comparator.comparing(
MyObj::getTime,Comparator.nullsLast(Instant::compareTo));

Comparator.nullsLast()
提供一个比较器,该比较器最后对空值进行排序,并使用作为参数提供的比较器比较非空对象。我还使用了两个arg
进行比较​(函数您没有履行
Comparator.compare()
的合同。如果没有您的
ConcurrentSkipSet
代码,我们无法判断这是否是问题的根源,但很可能是

如果两个
MyObj
都是
null
,一个是
null
,另一个是
null
瞬间,或者两者都是
null
瞬间,则比较器无法决定它们的顺序。如果跳过集调用
compare(a,b)
a
应该最后一个,但如果下一个调用
compare(b,a)
,突然间
b
应该排在最后。这会导致它循环吗?在任何情况下,比较器的属性不一致都不是好事,而且它肯定会导致使用
比较器的其他类出现问题

compare
方法的文档说明:

实现者必须确保所有
x
y
sgn(compare(x,y))==-sgn(compare(y,x))
(这意味着compare(x,y)必须在且仅在compare(y,x)时抛出异常 抛出异常。)

这是您正在破坏的部分。您的跳过集应该能够依靠您遵守合同

你说:

它们不能与我的逻辑和程序的工作方式相同 永远不应该平等

如果是这样,您需要为任意两个
MyObj
确定一个顺序。一个解决方案是不允许空值(如果出现
null
则引发异常)。另一个解决方案是确定空值相等

定义将null放在最后的比较器的最简洁方法是通过
comparator.nullsLast()
comparator.comparating()
(自Java 8以来):

Comparator comp=Comparator.nullsLast(Comparator.comparing(
MyObj::getTime,Comparator.nullsLast(Instant::compareTo));

Comparator.nullsLast()
提供了一个比较器,该比较器最后对null进行排序,并使用作为参数提供的比较器比较非null对象。我还使用了两个参数进行比较​(函数两个实例永远不能与您的逻辑相等是的,它们不能与我的逻辑相等,并且程序的工作方式也不应该相等。每个实例都设置为当前时间,并且没有两个实例是同时创建的。因此,您将该集纯粹用作不同对象的排序列表?您永远不会使用设置的事实消除重复
    Comparator<MyObj> comp = Comparator.nullsLast(Comparator.comparing(
            MyObj::getTime, Comparator.nullsLast(Instant::compareTo)));