Сортировать набор объектов Java по одному значению и оставаться уникальным по другому значению

Мне нужно отсортировать коллекцию объектов java по целочисленному значению «уровень». Мне также нужно определить, содержит ли эта коллекция уже объект по «названию».

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

У меня есть объект с атрибутами «уровень» и «название». Он реализует сопоставимые значения:

Он переопределяет метод Equals (для проверки того, содержится ли объект уже в TreeSet по «названию».

Код выглядит так:

@Override
public boolean equals(Object arg0) {

    Artifact obj = (Artifact) arg0;

    if (this.getTitle().equals(obj.getTitle())) { 
        return true;
    }

    return false;
}

@Override
public int compareTo(Artifact aThat) {  

    final int BEFORE = -1;
    final int EQUAL = 0;
    final int AFTER = 1;

    if (this == aThat) return EQUAL;

    if (this.level < aThat.level) return BEFORE;
    if (this.level > aThat.level) return AFTER;

    assert this.equals(aThat) : "compareTo inconsistent with equals.";
    return EQUAL;
}

Когда я пытаюсь добавить значения в список из массива с возможными повторяющимися значениями. Кажется, что содержимое не работает, и объекты добавляются в TreeSet независимо. Вот код:

TreeSet<Artifact> subsetOfArtifacts = new TreeSet<Artifact>();

ArrayList<Artifact> allArtifacts = getArtifacts(); 
Iterator<Artifact> allArtifactsIter = allArtifacts.iterator();

while (allArtifactsIter.hasNext()) {
    Artifact artifact = (Artifact) allArtifactsIter.next();
    if (!subsetOfArtifacts.contains(artifact)) {
        subsetOfArtifacts.add(artifact);
    }
 }

В идеале я хочу иметь список всех уникальных артефактов, отсортированных по уровню. Как мне это сделать?


person Atma    schedule 09.05.2012    source источник
comment
Это также заставит вас иметь уникальные уровни. Лучшим решением, вероятно, является очевидный, простой, многоэтапный подход, а не попытка собрать все это в одну структуру данных.   -  person Louis Wasserman    schedule 10.05.2012
comment
вы включили утверждения во время работы с переключателем -ea   -  person ratchet freak    schedule 10.05.2012


Ответы (4)


Если вы переопределяете equals() следует переопределить hashCode() тоже! В противном случае поведение с коллекциями, особенно с наборами, не определено. Вы должны добавить этот метод в свой класс:

@Override
public int hashCode() {
    return title.hashCode();
}

Затем вы должны вместо этого использовать HashSet и использовать Set sortedSet = new TreeSet(set); для сортировки. Как только вы это сделаете, все должно работать нормально.

Причина в том, что HashTables полагаются на тот факт, что если два объекта равны equal(), то их hashCode() также равны. Вот выдержка из javadoc для hashCode()

Общий контракт hashCode:

  • Всякий раз, когда он вызывается для одного и того же объекта более одного раза во время выполнения приложения Java, метод hashCode должен постоянно возвращать одно и то же целое число, при условии, что никакая информация, используемая в сравнениях на равенство для объекта, не изменяется. Это целое число не обязательно должно оставаться постоянным от одного выполнения приложения к другому выполнению того же приложения.
  • Если два объекта равны в соответствии с методом equals(Object), то вызов метода hashCode для каждого из двух объектов должен давать одинаковый целочисленный результат.
  • Не требуется, чтобы, если два объекта не были равны в соответствии с методом equals(java.lang.Object), то вызов метода hashCode для каждого из двух объектов должен давать разные целочисленные результаты. Однако программист должен знать, что получение различных целочисленных результатов для неравных объектов может повысить производительность хеш-таблиц.
person Bohemian♦    schedule 09.05.2012
comment
какой хешсет? он говорит только о деревьях - person ratchet freak; 10.05.2012
comment
Нет необходимости использовать HashSet, если ему нужен TreeSet. - person rob; 10.05.2012

CompareTo нужен не только для сравнения уровня, но и для сравнения заголовка, так как equals сравнивает заголовок.

public int compareTo(Artifact aThat) {

    final int BEFORE = -1;
    final int EQUAL = 0;
    final int AFTER = 1;

    if ( this == aThat ) return EQUAL;

    if (this.level < aThat.level) return BEFORE;
    if (this.level > aThat.level) return AFTER;
    return this.getTitle().compareTo(aThat.getTitle());

//        assert this.equals(aThat) : "compareTo inconsistent with equals.";

//    return EQUAL;
}

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

person rob    schedule 09.05.2012

RTFM: D javadoc для TreeSet четко заявляет: «Обратите внимание, что порядок, поддерживаемый набором (независимо от того, предоставляется ли явный компаратор или нет), должен быть совместим с равными, если он должен правильно реализовать интерфейс Set»

ваши равные и компаратор несовместимы. тот, который вам нужно добавить, а затем вам придется его отсортировать.

Возможно, вам потребуется создать собственную реализацию для вашего варианта использования.

и, как сказали другие ребята: если вы меняете равенство, всегда меняйте хэш-код.

2 одинаковых объекта должны производить одинаковый хэш-код.

person ZeoS    schedule 09.05.2012

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

@Override
public int compareTo(Artifact aThat) 
{
    final int BEFORE = -1;
    final int EQUAL = 0;
    final int AFTER = 1;

    if ( this == aThat ) return EQUAL;

    if( this.level < aThat.level ) return BEFORE;
    if( this.level > aThat.level ) return AFTER;

    int compare = this.getTitle().compareTo(aThat.getTitle());

    if( compare != EQUAL ) return compare;

    assert this.equals(aThat) : "compareTo inconsistent with equals.";

    return EQUAL;
}

Не видел ответа Роба перед публикацией.

person Martin    schedule 09.05.2012