Компаратор для TreeSet не работает должным образом

Моя идея состояла в том, чтобы отсортировать некоторые объекты (которые являются просто целыми числами) по их слоям. Я нашел кого-то, у кого была такая же проблема (я думаю), но я не понял решения. Слои каждого объекта хранятся в массиве. Я использовал перечисление для слоев.

public enum Layer {
DEFAULT,
BACKGROUND,
FOREGROUND,
HUD_0,
HUD_1
}

Поэтому я не знаю, неправильно ли я понял, как работает компаратор, или у меня просто проблемы, потому что сущности являются целыми числами...

final Layer[] layers = {Layer.BACKGROUND, Layer.HUD_1, Layer.DEFAULT, Layer.DEFAULT, Layer.HUD_0, Layer.HUD_1};
    TreeSet<Integer> sorted = new TreeSet<Integer>(new Comparator<Integer>() {
        @Override
        public int compare(Integer entity1, Integer entity2) {
            //Integer layer1 = layers[entity1].ordinal();
            //Integer layer2 = layers[entity2].ordinal();
            //return layer1 < layer2 ? -1 : (layer1 > layer2 ? 1 : 0);
            return layers[entity1].compareTo(layers[entity2]);
        }
    });
    sorted.add(0);//bg
    System.out.println(sorted.toString());
    sorted.add(4);//hud0
    System.out.println(sorted.toString());
    sorted.add(2);//def
    System.out.println(sorted.toString());
    sorted.add(3);//def
    System.out.println(sorted.toString());
    sorted.add(1);//hud1
    System.out.println(sorted.toString());
    sorted.add(5);//hud1
    System.out.println(sorted.toString());

Это результат:

[0]
[0, 4]
[2, 0, 4]
[2, 0, 4]
[2, 0, 4, 1]
[2, 0, 4, 1]

последний на самом деле должен быть [2, 3, 0, 4, 1, 5]

Я читал, что TreeSet хранит каждый элемент только один раз, поскольку это набор, но я не знаю, почему для этого используется слой, а не объекты, которые я действительно хочу вставить.


person jan    schedule 02.07.2013    source источник


Ответы (3)


С точки зрения TreeSet два элемента равны, если item1.compareTo(item2) == 0. В вашем случае Layer.HUB_1 появляется дважды в вашем массиве, и оба элемента будут считаться равными, поэтому будет добавлен только один.

person assylias    schedule 02.07.2013
comment
ах хорошо, это объясняет вывод. У вас также есть решение, как я могу это исправить? Я просто хочу отсортировать объекты по их слоям. - person jan; 02.07.2013
comment
@jan Вы можете добавить условие в свой компаратор, например, if (layers[entity1].compareTo(layers[entity2]) == 0) return entity1.compareTo(entity2);. - person assylias; 02.07.2013

Это происходит потому, что именно так определяется TreeSet: «экземпляр TreeSet выполняет все сравнения элементов, используя свой метод compareTo (или сравнение), поэтому два элемента, которые считаются равными с помощью этого метода, с точки зрения набора равны. " java.util.TreeSet

Вы можете ввести прерыватель связи, который будет устанавливать порядок между различными объектами в одном и том же слое — это может быть просто произвольный порядковый номер, если вы не можете использовать что-либо более значимое. Объекты в разных слоях по-прежнему будут упорядочены по слоям.

person Patricia Shanahan    schedule 02.07.2013
comment
Это возможное решение. Спасибо за это. Я попробую прямо сейчас :) хотя я понятия не имею, как xD - person jan; 02.07.2013
comment
@jan Перед возвратом результата проверьте, равен ли необработанный результат сравнения нулю с неравными ссылками. Если он равен нулю, используйте средство разрешения конфликтов, чтобы выбрать -1 или +1. - person Patricia Shanahan; 02.07.2013

В конечном итоге проблема заключается в том, что TreeSet (будучи набором) будет содержать элементы только один раз, и ему нужен способ узнать, содержит ли он уже элемент, и для этого он использует предоставленный вами Компаратор. Поскольку ваш компаратор использовал перечисления для упорядочения, Treeset считал две записи равными, если перечисления совпадали.

Подробности см. на http://docs.oracle.com/javase/6/docs/api/java/util/TreeSet.html, особенно абзац

Обратите внимание, что порядок, поддерживаемый набором (независимо от того, предоставлен явный компаратор или нет), должен быть согласован с equals, если он должен правильно реализовать интерфейс Set. (См. Comparable или Comparator для точного определения соответствия с равными.) Это так, потому что интерфейс Set определен в терминах операции равенства, но экземпляр TreeSet выполняет все сравнения элементов, используя свой метод compareTo (или сравнение), поэтому два элементы, которые считаются равными по этому методу, с точки зрения множества равны. Поведение набора четко определено, даже если его порядок несовместим с равными; он просто не подчиняется общему контракту интерфейса Set.

В конечном счете, если вы хотите использовать TreeSet для сортировки перечисления, вам потребуется расширить логику сравнения, чтобы вернуться к чему-то другому, когда перечисления совпадают. Что-то типа

public int compare(Integer entity1, Integer entity2) {
    int returnValue = layers[entity1].compareTo(layers[entity2]);
    if(returnValue == 0){
        returnValue = entity1.compareTo(entity2);
    }
}

Что затем приводит к тому, что объекты не совпадают (и не могут быть добавлены в TreeSet) только потому, что совпадают перечисления.

person Billy Mailman    schedule 02.07.2013
comment
Как уже указывалось, многого из этого можно избежать, поскольку перечисления уже Comparable в java. Я уже исправил это, но это был всего лишь избыточный код. Проблема все та же. - person jan; 02.07.2013
comment
Ага, спасибо, я тоже так делал :) assylias была немного быстрее - person jan; 02.07.2013