Есть ли набор, сохраняющий порядок вставки, который также реализует List?

85

Я пытаюсь найти реализацию java.util.Listи java.util.Setодновременно на Java. Я хочу, чтобы этот класс разрешал только уникальные элементы (как Set) и сохранял их порядок (например List). Он существует в JDK 6?

Это важно, List<T>#add(int, T)чтобы я мог вставлять в определенную позицию.

Егор256
источник
Вы имеете в виду сохранить их порядок вставки или какой-то порядок, определенный a Comparator? Также вам нужна семантика Listинтерфейса?

Ответы:

207

TreeSetсортируется по порядку элементов; LinkedHashSetсохраняет порядок размещения. Надеюсь, один из них - то, что вам нужно.

Вы указали, что хотите иметь возможность вставлять в произвольное место, я подозреваю, что вам придется написать свое собственное - просто создайте класс, содержащий a HashSet<T>и an ArrayList<T>; при добавлении элемента проверьте, есть ли он в наборе, прежде чем добавлять его в список.

В качестве альтернативы общие коллекции Apache4 предлагают ListOrderedSetи SetUniqueList, которые ведут себя аналогичным образом и должны соответствовать заданным требованиям.

Джон Скит
источник
12

Вы имеете ввиду нравится LinkedHashSet? Это сохраняет порядок ввода, но не допускает дублирования.

IMHO, это необычное требование, но вы можете написать список без дубликатов.

class SetList<T> extends ArrayList<T> {
    @Override
    public boolean add(T t) {
        return !super.contains(t) && super.add(t);
    }

    @Override
    public void add(int index, T element) {
        if (!super.contains(element)) super.add(index, element);
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
        boolean added = false;
        for (T t : c)
            added |= add(t);
        return added;
    }

    @Override
    public boolean addAll(int index, Collection<? extends T> c) {
        boolean added = false;
        for (T t : c)
            if (!super.contains(t)) {
                super.add(index++, t);
                added = true;
            }
        return added;
    }
}
Питер Лоури
источник
Он не реализует Listинтерфейс, см. Мои изменения в вопросе
yegor256
2
stackoverflow.com/a/8185105/253468 выглядит лучше, потому что у него нет O(n)сложности вставки, есть компромисс между двойным хранением и O(log(n))операцией вставки.
TWiStErRob
8

Невозможно реализовать Listи Setсразу без нарушения договора. См., Например, Set.hashCodeдоговор:

Хэш-код набора определяется как сумма хэш-кодов элементов в наборе, где хэш-код нулевого элемента определяется как нулевой.

С другой стороны, вот договор List.hashCode:

Хэш-код списка определяется как результат следующих вычислений:

int hashCode = 1;
for (E e : list)
    hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());

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

Тагир Валеев
источник
4

Если вы не ограничиваетесь JDK 6, вы можете использовать общую библиотеку коллекций Apache, которая предлагает точное соответствие вашим потребностям - ListOrderedSet . Это вроде Listи Setсовмещено вместе :)

Ондрей Бозек
источник
минус Listинтерфейс
LateralFractal
0

У меня была похожая проблема, поэтому я написал свою. Смотрите здесь . IndexedArraySetРаспространяется ArrayListи инвентарь Set, поэтому он должен поддерживать все операции , которые вам нужны. Обратите внимание, что вставка элементов в места в середине ArrayListможет быть медленной для больших списков, потому что все следующие элементы необходимо переместить. Мой IndexedArraySetне меняет этого.

ЯнКанис
источник
0

Другой вариант (без Listтребования к интерфейсу) - это Guava ImmutableSet, который сохраняет порядок вставки. Со своей вики-страницы :

За исключением отсортированных коллекций, порядок сохраняется со времени строительства. Например,

ImmutableSet.of("a", "b", "c", "a", "d", "b")

будет перебирать свои элементы в порядке «a», «b», «c», «d».

купорический
источник