我正在尝试以非常简洁的方式(当然使用流)来确定集合中的两个成员(列表)是否相等。但在方法equals()
的意义上并不相同,而是使用不同的方法进行比较。
基本上,我有一个时间列表,这些定义了一个订单。您可以使用Timeslot#compareTo(Timeslot)
方法说明时间段是先于,超过或等于另一个时间段。我需要这些时间段按顺序排列,也就是说,它们中的每一个都在列表中的后面,但我还需要它们中的每一个都严格地大于下一个。它们不能相等(您不能拥有timeslot1=10am
然后timeslot2=10am
)。
我完成了订购,这很简单:
Collections.sort(timeslots, Timeslot::compareTo);
但是,在确定每个时隙是否严格优先于以下时,我想非常简洁。当然,我可以按照以下传统方式做到这一点:
for (int i = 0; i < timeslots.size() - 1; i++)
if (timeslots.get(i).compareTo(timeslots.get(i + 1)) == 0)
throw new IllegalArgumentException("Every timeslot must strictly precede the following");
也许我要求过多的流,并没有更好,更有效的方法来实现这一点,但我很乐意听到你的想法。
答案 0 :(得分:4)
通过尝试查找Stream API解决方案,您可能认为太复杂了。使用基本的Collection API操作可以轻松解决该任务:
List<Timeslot> timeslots;// the source
TreeSet<Timeslot> sorted=new TreeSet<>(timeslots);
if(sorted.size() != timeslots.size())
throw new IllegalArgumentException("Every timeslot must strictly precede the following");
// get a list where every timeslot strictly precedes the following:
timeslots=new ArrayList<>(sorted);
TreeSet
元素的相等性仅由顺序决定。因此,如果根据顺序存在重复项,并且这就是您的问题所在,则在将列表转换为集合时会删除它们。因此,如果大小不同,就会出现这样的重复。如果不需要,您可以将已排序的集合转换回列表,如果需要列表。否则,您可以继续使用该集而不是列表。
应该提到的是,由于您声明订单与equals不一致,因此TreeSet
也不会完全符合its documentation合同中所述的Set
合同}:
请注意,如果要正确实现
Set
接口,则由集合维护的排序(无论是否提供显式比较器)必须与equals 一致。 [...]这是因为 Set
接口是根据equals
操作定义的,但TreeSet
实例使用其compareTo
(或{{compare
执行所有元素比较1}})方法,因此,从集合的角度来看,这种方法认为相等的两个元素是相等的。集合的行为定义良好,即使它的排序与equals不一致;它只是没有遵守Set
接口的一般合同。
由于您的操作不依赖于Set
接口的常规合同,因为之前没有Set
,因此对于此特定用例而言,这不是问题。转换为如上所示的List
是有效的,因此会迭代元素,以防您在没有转换的情况下使用它,即不需要特定的列表操作。
答案 1 :(得分:2)
我认为对列表进行排序并比较相邻元素是一种合理的方法。以下是基于流的代码:
timeslots.sort(null); // sort the list using natural order
if (IntStream.range(1, timeslots.size())
.map(i -> timeslots.get(i-1).compareTo(timeslots.get(i)))
.anyMatch(i -> i == 0)) {
throw new IllegalArgumentException("...");
}
它并不比你拥有的短,但基于流的方法可能提供更大的灵活性:它可以并行运行,你可以计算重复时间戳的数量等。
您还可以将compareTo()
电话内联到anyMatch()
来电,如下所示:
if (IntStream.range(1, timeslots.size())
.anyMatch(i -> timeslots.get(i-1).compareTo(timeslots.get(i)) == 0) {
我认为这是否更可取是一种风格问题。
答案 2 :(得分:2)
简单 - 只需使用Stream
'sorted'并制作自定义比较器。
请参阅https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#sorted-java.util.Comparator-
这样的事情:
Timeslots timeslots = ...
Stream<Timeslots> tstream = timeslots
.stream()
.sorted( (a,b) -> {
int compare = a.compareTo(b);
if(compare==0){
throw new IllegalArgumentException("Every timeslot must strictly precede the following");
}
return compare;
});
是的......你需要一个终端操作来实际运行Stream。 我假设有下一个操作或者你给Timeslots提供的东西。
使用此方法可以获得Stream
结果,并且无需事先对Timeslots进行预排序。
试一试!
答案 3 :(得分:0)
我不确定这比你的版本更简洁,但是你可以用流来做到这一点:
timeslots.stream().reduce((a, b) -> {
if (a.compareTo(b) == 0) {
throw new IllegalArgumentException("Every timeslot must strictly precede the following");
}
return b;
});
另一种解决方案,受@ VGR评论的启发:
timeslots.stream().collect(Collectors.toMap(Function.identity(), Function.identity(), (a, b) -> {
throw new IllegalArgumentException("Every timeslot must strictly precede the following");
}, () -> new TreeMap<>(Timeslot::compareTo)));