CompareTo可能返回0,替代TreeSet / TreeMap

时间:2010-06-14 20:46:49

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

我需要一组有序的对象,目前正在使用TreeSet。我的问题是对象的compareTo通常会返回0,这意味着这两个对象的顺序保持不变。 TreeMap(默认情况下由TreeSet使用)会将它们视为同一个对象,但这不是真的。

我可以使用TreeMap的替代方案吗?


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

5 个答案:

答案 0 :(得分:9)

您要定义一个要比较的条件,但您需要添加额外的条件。

你说:

  

我有一组可显示的对象。我想按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。如果其中一个属性与其他属性相同,则可能不应移动它们。如果有其他属性要比较使用它。

答案 1 :(得分:4)

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

重写你的compareTo,以便不相等的元素返回不同的值,或使用类似java.util.PriorityQueue的内容,允许多个相等元素的副本。

答案 2 :(得分:1)

我以前做过这个。它是一个有序的多映射,它只是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]}

答案 3 :(得分:0)

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

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 =添加到集合
  • 时分配给对象的序列号

答案 4 :(得分:0)

使用有序集(例如TreeSet)时,要记住两件重要的事情:

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

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

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

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