【问题标题】:CompareTo may return 0, alternative to TreeSet/TreeMapCompareTo 可能返回 0,替代 TreeSet/TreeMap
【发布时间】:2011-03-03 17:13:26
【问题描述】:

我需要一组已排序的对象,目前正在使用TreeSet。我的问题是对象的compareTo 通常会返回0,这意味着这两个对象的顺序保持不变。 TreeMapTreeSet 默认使用)会将它们视为同一个对象,这是不正确的。

我可以使用 TreeMap 的替代品吗?


用例:我有一组可显示的对象。我想按 Y 坐标对它们进行排序,以便它们以正确的顺序呈现。当然,两个对象很可能有相同的 Y 坐标。

【问题讨论】:

  • 如果两个元素有相同的 Y 坐标,你先放什么?还有其他标准吗?

标签: java sorting collections binary-tree red-black-tree


【解决方案1】:

您正在定义一个要比较的标准,但您需要添加额外的标准。

你说:

我有一组可显示的对象。我想按 Y 坐标对它们进行排序,以便它们以正确的顺序呈现。当然,两个对象很可能有相同的 Y 坐标。

那么,如果两个元素有相同的 Y 坐标,你先放什么?其他标准是什么?

可能是创建时间,也可能是x坐标,只要定义就好:

Map<String,Thing> map = new TreeMap<String,Thing>(new Comparator<Thing>(){
     public int compare( Thing one, Thing two ) {
         int result = one.y - two.y;
         if( result == 0 ) { // same y coordinate use another criteria
             result = one.x - two.x;
             if( result == 0 ) { //still the same? Try another criteria ( maybe creation time
                 return one.creationTime - two.creationTime
             }
          }
          return result;
     }
});

您必须定义一个 Thing 何时高于/低于/等于/高于其他 Thing 。如果其中一个属性与其他属性相同,则可能您不应该移动它们。如果有其他属性比较使用它。

【讨论】:

  • 一个 if 语句!为什么我没有想到这一点,而是写了一个丑陋的移位黑客:p
  • 大声笑...有时我们的思维迭代得太远了,我们只需要外部的人让我们看向明显的方向。 我的眼镜呢? - 嗯...你穿着它们有点:)
【解决方案2】:

您遇到的问题是 compareTo 返回 0 意味着对象是相等的。同时,您将它们放入一个集合中,这不允许相同元素的多个副本。

要么重写你的compareTo,让不相等的元素返回不同的值,要么使用java.util.PriorityQueue之类的东西,它允许相等元素的多个副本。

【讨论】:

    【解决方案3】:

    我以前做过。它是一个有序的多图,它只是一个 List 对象的 TreeMap。像这样..

    Map<KeyType, List<ValueType>> mmap = new TreeMap<KeyType, List<ValueType>>();
    

    每次引入新键时,您都需要构造一个新的 LinkedList,因此将其包装在自定义容器类中可能会有所帮助。我会试着找到一些东西。


    所以,我很快将这个自定义容器放在一起(完全未经测试),但它可能正是您要找的。请记住,如果您确实在寻找值列表的有序映射,则应该只使用这种类型的容器。如果您的值有一些自然顺序,您应该按照其他人的建议使用 TreeSet。

    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    import java.util.TreeMap;
    
    public class MTreeMap<K, V> {
    
       private final Map<K, List<V>> mmap = new TreeMap<K, List<V>>();
       private int size = 0;
    
       public MTreeMap() {
       }
    
       public void clear() {
          mmap.clear();
          size=0;
       }
    
       public boolean containsKey(K key) {
          return mmap.containsKey(key);
       }
    
       public List<V> get(K key) {
          return mmap.get(key);
       }
    
       public boolean isEmpty() {
          return mmap.isEmpty();
       }
    
       public Set<K> keySet() {
          return mmap.keySet();
       }
    
       public Collection<List<V>> valueLists() {
          return mmap.values();
       }
    
       public void put(K key, V value) {
    
          List<V> vlist = mmap.get(key);
          if (null==vlist) {
             vlist = new LinkedList<V>();
             mmap.put(key, vlist);
          }
          vlist.add(value);
          ++size;
       }
    
       public List<V> remove(Object key) {
          List<V> vlist = mmap.remove(key);
    
          if (null!=vlist) { 
             size = size - vlist.size() ;
          }
          return vlist;
       }
    
       public int size() {
          return size;
       }
    
       public String toString() {
          return mmap.toString();
       }
    
    }
    

    这是一个初步的测试:

    public class TestAnything {
    
       public static void main(String[] args) {
    
          MTreeMap<Integer, String> mmap  = new MTreeMap<Integer, String>();
    
          mmap.put(1, "Value1");
          mmap.put(2, "Value2");
          mmap.put(3, "Value3");
          mmap.put(1, "Value4");
          mmap.put(3, "Value5");
          mmap.put(2, "Value6");
          mmap.put(2, "Value7");
    
          System.out.println("size (1) = " + mmap.get(1).size());
          System.out.println("size (2) = " + mmap.get(2).size());
          System.out.println("size (3) = " + mmap.get(3).size());
          System.out.println("Total size = " + mmap.size());
    
          System.out.println(mmap);
       }
    
    }
    

    输出是这样的:

    size (1) = 2
    size (2) = 3
    size (3) = 2
    Total size = 7
    {1=[Value1, Value4], 2=[Value2, Value6, Value7], 3=[Value3, Value5]}
    

    【讨论】:

      【解决方案4】:

      我有一个自己的想法,但这更像是一种解决方法

      int compare(Object a, Object b) {
         an = a.seq + (a.sortkey << 16); // allowing for 65k items in the set
         bn = b.seq + (a.sortKey << 16);
         return an - bn; // can never remember whether it's supposed to be this or b - a.
      }
      
      • sortKey = 排序中真正重要的内容,例如 Y 坐标
      • seq = 添加到集合时分配给对象的序列号

      【讨论】:

        【解决方案5】:

        使用排序集(例如 TreeSet)时要记住两点重要的事情:

        1) 它们是集合;同一个集合中不允许有两个相等的元素

        2) 相等性必须与比较机制一致(比较器或可比较)

        因此,在您的情况下,您应该通过添加一些辅助排序标准来“打破平局”。例如:首先使用 Y 轴,然后使用 X,然后使用一些唯一的对象标识符。

        另见http://eyalsch.wordpress.com/2009/11/23/comparators/

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-04-10
          • 2023-03-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-11-08
          • 1970-01-01
          相关资源
          最近更新 更多