【问题标题】:Java Set strange behaviourJava设置奇怪的行为
【发布时间】:2012-09-30 00:04:21
【问题描述】:

我遇到了一个问题,我需要根据某些条件更改已排序集的可比较值。

做这样的事情:

SortedSet<T> groups;

for(T t: groups){
        t.setOrdinal(max);
}

不会自动重新排列 SortedSet。

在线阅读我发现我需要从集合中删除对象,然后再次添加它。显然,在迭代集合时我不能这样做。所以我决定制作一个数组列表。使集合为空,然后再次添加所有元素,以便它们遵循排序顺序。 我做了这样的事情:

SortedSet 组;

List<T> groupList = new ArrayList<T>(groups);
groups = null;
for(T t: groupList){
        t.setOrdinal(max);
}

groups = new TreeSet<T>(groupList);

但是当我检查组集时,它没有遵循基于比较对象 T 序数的比较器的排序顺序

但是当我尝试这样的事情时:

SortedSet 组;

List<T> groupList = new ArrayList<T>(groups);
groups.clear();
for(T t: groupList){
        t.setOrdinal(max);
}

groups.addAll(groupList);

我得到了预期的结果。有人可以解释一下发生了什么吗?

这就是我实现类 T 的方式

public class T implements Serializable, Comparable<T> {
//
int ordinal;
//getter
//setter

 @Override
  public int compareTo(T that) {
    if (that == null) {
      return 1;
    }

    return this.ordinal - that.ordinal;
  }

}

想看完整节目的朋友:

List<SmartUser> groupsList = new ArrayList<SmartUser>(groups);
groups = null;
for (SmartUser smartUser : groupsList) {
        if (smartUser.isExpired()) {
                smartUser.setOrdinal(Long.MAX_VALUE);
        }
        SmartUserQuery smartUserQuery = smartUser.getSmartUserQuery();
        if (smartUserQuery != null) {
                //Do parallel processing: of each samrtUser
        }
}

groups = new TreeSet<SmartUser>groupsList;

正确的结果给出方法:

List<SmartUser> groupsList = new ArrayList<SmartUser>(groups);
groups.clear();
for (SmartUser smartUser : groupsList) {
        if (smartUser.isExpired()) {
                smartUser.setOrdinal(Long.MAX_VALUE);
        }
        SmartUserQuery smartUserQuery = smartUser.getSmartUserQuery();
        if (smartUserQuery != null) {
                //Do parallel processing: of each samrtUser
        }
}

groups.addAll(groupsList);

谢谢。

【问题讨论】:

  • 请展示一个简短但完整的程序来说明问题。例如,我们不知道您的 SortedSet&lt;T&gt; 是使用自定义比较器还是普通的 Comparable&lt;E&gt; 实现。
  • 从您介绍的方式来看,这两种方式应该都可以正常工作。
  • 除此之外,您的 compareTo 方法由于溢出而被破坏......并且您显示的示例会将 every 值的序数设置为相同价值,所以任何订单都是可以接受的。再次,请简短但完整的程序...
  • 您的预期结果是什么?
  • 你能描述一下你实际上想用这个做什么吗?试图将其压缩到 SortedSet 中只会导致脆弱、容易损坏的代码。

标签: java iterator set treeset


【解决方案1】:

我发现我需要从集合中移除对象,然后 再次添加。显然,在迭代集合时我不能这样做。 所以我决定做一个集合的Arraylist。

看看我在maintaining TreeSet sort as object changes value 的回答并尝试我的UpdateableTreeSet 实现。它允许您在遍历排序集时执行延迟更新。

【讨论】:

    【解决方案2】:

    将您的 compareTo 方法更改为以下

    @Override
    public int compareTo(CustomObject o) {
        return Integer.valueOf(o.ordinal).compareTo(ordinal );
    }
    

    此外,根据TreeSet 合同,您应该提供与 compareTo 方法同步的 equals 方法

     @Override
    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        if (!(obj instanceof CustomObject))
            return false;
        CustomObject o = (CustomObject) obj;
    
        return this.ordinal == o.ordinal;
    }
    

    示例实现

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Set;
    import java.util.TreeSet;
    
    public class CustomObject implements Comparable<CustomObject> {
    
    private int ordinal = 0;
    
    public CustomObject(int priority) {
        this.ordinal = priority;
    }
    
    /**
     * @param args
     */
    public static void main(String[] args) {
    
        List<CustomObject> list = new ArrayList<CustomObject>();
        list.add(new CustomObject(1));
        list.add(new CustomObject(2));
        list.add(new CustomObject(3));
        list.add(new CustomObject(6));
        list.add(new CustomObject(4));
        System.out.println("Before: " + list);
        for (CustomObject object : list) {
            if (object.ordinal == 4) {
                object.ordinal = 10;
            }
        }
        Set<CustomObject> set = new TreeSet<CustomObject>();
        set.addAll(list);
        System.out.println("After: " + set);
    
    }
    
    @Override
    public int compareTo(CustomObject o) {
        return Integer.valueOf(o.ordinal).compareTo(ordinal);
    }
    
    @Override
    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        if (!(obj instanceof CustomObject))
            return false;
        CustomObject o = (CustomObject) obj;
    
        return this.ordinal == o.ordinal;
    }
    
    @Override
    public String toString() {
        return " Ordinal Value is :" + ordinal;
    }
    
    }
    

    【讨论】:

    • 您确实需要重写 .equals 方法,但这也意味着您也应该重写 .hashcode 方法,否则您的整个操作将崩溃。哈希集使用内部哈希图进行操作,如果 .equals 方法和 .hashcode 方法不一致(即,用于确定相等性的任何字段都需要成为哈希码生成的一部分),数据结构就会崩溃。在您的情况下,您可以将该方法委托给 Integer.valueOf(ordinal).hashcode()
    • @Matt 问题是关于 TreeSet 的,所以我现在确实包含了 hashCode,因为只有 HashSet 和 HashMap 才需要散列。但是是的,它很好,而且很容易添加。
    • 我认为我的 compareTo 方法很好,因为我确信序数总是非负数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 2018-08-15
    • 2017-09-27
    • 2015-12-04
    相关资源
    最近更新 更多