Java TreeSet Comparator在某些情况下无法删除重复项?
我的树集有以下比较器:Java TreeSet Comparator在某些情况下无法删除重复项?,java,sorting,comparator,treeset,Java,Sorting,Comparator,Treeset,我的树集有以下比较器: public class Obj { public int id; public String value; public Obj(int id, String value) { this.id = id; this.value = value; } public String toString() { return "(" + id + value + ")"; } } O
public class Obj {
public int id;
public String value;
public Obj(int id, String value) {
this.id = id;
this.value = value;
}
public String toString() {
return "(" + id + value + ")";
}
}
Obj obja = new Obj(1, "a");
Obj objb = new Obj(1, "b");
Obj objc = new Obj(2, "c");
Obj objd = new Obj(2, "a");
Set<Obj> set = new TreeSet<>((a, b) -> {
System.out.println("Comparing " + a + " and " + b);
int result = a.value.compareTo(b.value);
if (a.id == b.id) {
return 0;
}
return result == 0 ? Integer.compare(a.id, b.id) : result;
});
set.addAll(Arrays.asList(obja, objb, objc, objd));
System.out.println(set);
公共类Obj{
公共int id;
公共字符串值;
公共对象(整数id,字符串值){
this.id=id;
这个值=值;
}
公共字符串toString(){
返回“(“+id+value+”)”;
}
}
Obj obja=新Obj(1,“a”);
Obj objb=新Obj(1,“b”);
Obj objc=新的Obj(2,“c”);
Obj objd=新的Obj(2,“a”);
集合集合=新树集合((a,b)->{
System.out.println(“比较“+a+”和“+b”);
int结果=a.value.compareTo(b.value);
如果(a.id==b.id){
返回0;
}
返回结果==0?整数。比较(a.id,b.id):结果;
});
addAll(Arrays.asList(obja、objb、objc、objd));
系统输出打印项次(套);
它打印出[(1a),(2c)],删除了重复项
但是,当我将最后一个整数.compare
更改为整数.compare(b.id,a.id)
(即切换a和b的位置)时,它会打印出[(2a)、(1a)、(2c)]。显然,相同的id 2出现了两次
如何修复比较器,使其始终根据id删除重复项,并根据值(升序)然后根据id(降序)对有序集进行排序?您在问:如何修复比较器,使其始终根据id删除重复项,并根据值(升序)然后根据id(降序)对有序集进行排序 你想让比较器
Obj.id
Obj.value
和Obj.id
Function<Obj, Integer> byId = o -> o.id;
Set<Obj> setById = new TreeSet<>(Comparator.comparing(byId));
Function<Obj, String> byValue = o -> o.value;
Comparator<Obj> sortingComparator = Comparator.comparing(byValue).thenComparing(Comparator.comparing(byId).reversed());
Set<Obj> setByValueAndId = new TreeSet<>(sortingComparator);
或者,如果不需要集合本身,但要按所需顺序处理元素,则可以使用流
:
Consumer<Obj> consumer = <your consumer>;
setById.stream().sorted(sortingComparator).forEach(consumer);
// instantiating one additional Obj and reusing those from the question
Obj obj3a = new Obj(3, "a");
// reusing sortingComparator from the code above
Set<Obj> set = Stream.of(obja, objb, objc, objd, obj3a)
.distinct()
.sorted(sortingComparator)
.collect(Collectors.toCollection(LinkedHashSet::new));
System.out.println(set); // [(3a), (1a), (2c)]
现在我们可以使用流
:
Consumer<Obj> consumer = <your consumer>;
setById.stream().sorted(sortingComparator).forEach(consumer);
// instantiating one additional Obj and reusing those from the question
Obj obj3a = new Obj(3, "a");
// reusing sortingComparator from the code above
Set<Obj> set = Stream.of(obja, objb, objc, objd, obj3a)
.distinct()
.sorted(sortingComparator)
.collect(Collectors.toCollection(LinkedHashSet::new));
System.out.println(set); // [(3a), (1a), (2c)]
运行代码一次,然后切换整数的操作数。比较。该开关会导致不同的比较路径。区别在于比较(2a)
和(1a)
时
在第一次运行中,(2a)
大于(1a)
,因此它将与下一个条目(2c)
进行比较。这将导致相等-找到重复项
在第二次运行中,(2a)
小于(1a)
。因此,(2a)
将作为下一个条目与上一个条目进行比较。但是(1a)
已经是最小的条目,并且没有以前的条目。因此,未发现(2a)
的重复项,并将其添加到集合中
问:你说一个比较器不能完成两项任务,我的第一个比较器实际上两项任务都做得很好。
是-但仅针对给定示例。像我一样将Obj obj3a
添加到集合中并运行代码。返回的排序集为:
[(1a), (3a), (2c)]
这违反了按id
降序的相等value
s排序的要求。现在它的升序是id
。运行我的代码,它返回正确的顺序,如上所示
一段时间前,我在与比较器搏斗时得到了以下评论:“……这是一个很好的练习,演示了手动比较器实现有多么棘手……”(在每种情况下,您希望得到的确切结果是什么?谢谢。这就是为什么我选择TreeSet来进行自定义和排序。但我不明白为什么不可能实现我想要的?相等部分在我的比较器中完成,因此返回0以删除重复项。只有在这之后才有排序逻辑(即不重复)。我扩展了我的答案。很棒的东西!我感谢您为我们找到解决方案所做的努力,但您能否评论一下在我的原始比较器中返回0的效果?我想这是我的主要问题:返回0不意味着在treeset比较器中删除重复项吗?如果是这样,为什么它没有正确完成工作?尽管你说一个比较器不能完成两个任务,但我的第一个比较器实际上正确地完成了两个任务。这让我很困惑,因此我提出了这个问题。我很可能会同意“永远不要在比较中做两件事”。对于“或者结果不能保证”这一部分,我不同意,因为在大多数情况下,结果应该是确定的。因此,结果可能是有保证的(因为它是可预测的),但它可能不是正确的。对我来说,主要结论是,学会理解我们想要使用或实施的东西的责任。这一责任可能取决于具体情况。用于初始化树集的比较器
负责区分元素和集合中的元素。排序(列表l)
负责它们的顺序。
[(1a), (3a), (2c)]