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

发布于 2024-09-05 19:56:59 字数 307 浏览 2 评论 0原文

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

我可以使用什么替代TreeMap


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

I need a sorted set of objects and am currently using the TreeSet. My problem is that the compareTo of the objects will often return 0, meaning the order of those two objects is to be left unchanged. TreeMap (used by TreeSet by default) will then regard them as the same object, which is not true.

What alternative to TreeMap can I use?


Use case: I have a set of displayable objects. I want to sort them by Y coordinate, so that they are rendered in the correct order. Of course, two objects may well have the same Y coordinate.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

装纯掩盖桑 2024-09-12 19:56:59

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

你说:

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

You're defining one criteria to compare, but you need to add extra criteria.

You say:

I have a set of displayable objects. I want to sort them by Y coordinate, so that they are rendered in the correct order. Of course, two objects may well have the same Y coordinate.

So, If two elements have the same Y coordinate, what you you put first? What would be the other criteria?

It may be the creation time, it may be the x coordinate, you just have to define it:

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;
     }
});

You have to define when one Thing is higher / lower / equal / than other Thing . If one of the attributes is the same as other, probably you should not move them. If is there other attribute to compare the use it.

甜`诱少女 2024-09-12 19:56:59

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

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

The issue you're running into is that compareTo returning 0 means that the objects are equal. At the same time, you're putting them into a set, which does not allow multiple copies of equal elements.

Either re-write your compareTo so that unequal elements return different values, or use something like a java.util.PriorityQueue which allows multiple copies of equal elements.

雨落□心尘 2024-09-12 19:56:59

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

I've done this before. It's an ordered multi-map and it is just a TreeMap of List objects. Like this..

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

You need to construct a new LinkedList every time a new key is introduced, so it might be helpful to wrap it in a custom container class. I'll try to find something.


So, I threw this custom container together quickly (completely untested), but it might be what you are looking for. Keep in mind that you should only use this type of container if you are truly looking for an ordered map of value lists. If there is some natural order to your values, you should use a TreeSet as others have suggested.

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();
   }

}

Here's a rudimentary test:

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);
   }

}

The output is this:

size (1) = 2
size (2) = 3
size (3) = 2
Total size = 7
{1=[Value1, Value4], 2=[Value2, Value6, Value7], 3=[Value3, Value5]}
天煞孤星 2024-09-12 19:56:59

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

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

I have one idea of my own, but it's more of a workaround

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 = what really matters for the sorting, for example an Y coordinate
  • seq = a sequence number assigned to objects when added to the set
天赋异禀 2024-09-12 19:56:59

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

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

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

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

另请参阅 http://eyalsch.wordpress.com/2009/11/23/comparators /

There are 2 important things to remember when using sorted sets (e.g. TreeSet) :

1) They are sets; two equal elements are not allowed in the same collection

2) Equality must be consistent with the comparison mechanism (either comparator or comparable)

Therefore, in your case you should "break ties" by adding some secondary ordering criteria. For example: first use Y axis, then X, and then some unique object identifier.

See also http://eyalsch.wordpress.com/2009/11/23/comparators/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文