Мне нужен отсортированный набор объектов, и в настоящее время использую TreeSet
. Моя проблема состоит в том что compareTo
из объектов будет часто возвращаться 0
, значение порядка тех двух объектов должно быть оставлено без изменений. TreeMap
(используемый TreeSet
по умолчанию), будет затем рассматривать их как тот же объект, который не верен.
Какая альтернатива TreeMap
я могу использовать?
Вариант использования: у Меня есть ряд визуализуемых объектов. Я хочу отсортировать их по координате Y, так, чтобы они были представлены в правильном порядке. Конечно, два объекта могут иметь ту же координату Y.
Вы определяете один критерий для сравнения, но вам нужно добавить дополнительные критерии.
Вы говорите:
У меня есть набор отображаемых объектов. Я хочу отсортировать их по координате 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;
}
});
Вы должны определить, когда одна Вещь
выше / ниже / равна / чем другая Вещь
. Если один из атрибутов такой же, как и другой, то, вероятно, не стоит их перемещать. Если есть другой атрибут для сравнения, используйте его.
Проблема, с которой вы столкнулись, заключается в том, что compareTo
, возвращающий 0
, означает, что объекты равны. В то же время вы помещаете их в набор, который не допускает множественных копий одинаковых элементов.
Либо перепишите свой compareTo
, чтобы неравные элементы возвращали разные значения, либо используйте что-то вроде java.util.PriorityQueue
, которое позволяет создавать несколько копий одинаковых элементов.
Я уже делал это раньше. Это упорядоченный мультимап, и он представляет собой просто TreeMap объектов List. Вот так...
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]}
При использовании отсортированных множеств (например, TreeSet) нужно помнить 2 важные вещи:
1) Это множества; два одинаковых элемента не могут находиться в одной коллекции
2) Равенство должно соответствовать механизму сравнения (либо компаратор, либо сравниваемые)
Поэтому в вашем случае нужно "разрушить связи", добавив некоторые вторичные критерии упорядочивания. Например: сначала использовать ось Y, затем X, а затем какой-нибудь уникальный идентификатор объекта.
See also http://eyalsch.wordpress.com/2009/11/23/comparators/
У меня есть своя идея, но это скорее обходной путь
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.
}