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)));