Подтвердить что ты не робот

LinkedHashSet - порядок вставки и дубликаты - сохраняйте новейшие "сверху",

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

set.add("one");
set.add("two");
set.add("three");
set.add("two");

LinkedHashSet напечатает:

one, two, three

Но мне нужно:

one, three, two

Что было бы лучшим решением здесь? Есть ли способ сбора/коллекций, который может это сделать или я должен выполнить его вручную?

4b9b3361

Ответ 1

Большинство Коллекции Java могут быть расширены для настройки.

Подкласс LinkedHashSet, переопределяя add.

class TweakedHashSet<T> extends LinkedHashSet<T> {

    @Override
    public boolean add(T e) {
        // Get rid of old one.
        boolean wasThere = remove(e);
        // Add it.
        super.add(e);
        // Contract is "true if this set did not already contain the specified element"
        return !wasThere;
    }

}

Ответ 2

Вы можете просто использовать специальную функцию LinkedHashMap:

Set<String> set = Collections.newSetFromMap(new LinkedHashMap<>(16, 0.75f, true));
set.add("one");
set.add("two");
set.add("three");
set.add("two");
System.out.println(set); // prints [one, three, two]

В Oracles JRE LinkedHashSet в любом случае поддерживается LinkedHashMap, поэтому не так много функциональной разницы, но используемый здесь специальный конструктор настраивает LinkedHashMap, чтобы изменить порядок при каждом доступе не только при вставке. Это может показаться слишком большим, но на самом деле влияет только на вставку уже содержащихся ключей (значения в смысле Set). Другие затронутые операции Map (а именно get) не используются возвращенным Set.

Если вы не используете Java 8, вам нужно немного помочь компилятору из-за ограниченного вывода типа:

Set<String> set
    = Collections.newSetFromMap(new LinkedHashMap<String, Boolean>(16, 0.75f, true));

но функциональность одинаков.

Ответ 3

При инициализации вы LinkedHashSet вы можете переопределить метод добавления.

Set<String> set = new LinkedHashSet<String>(){
    @Override
    public boolean add(String s) {
        if(contains(s))
            remove(s);
        return super.add(s);
    }
};

Теперь он дает вам:

set.add("1");
set.add("2");
set.add("3");
set.add("1");
set.addAll(Collections.singleton("2"));

// [3, 1 ,2]

работает даже метод addAll.

Ответ 4

Все предлагаемое выше решение отлично, но если мы не хотим переопределять уже реализованные коллекции. Мы можем решить эту проблему, просто используя ArrayList с небольшим трюком

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

public static <T> void addToList(List<T> list, T element) {
    list.remove(element); // Will remove element from list, if list contains it
    list.add(element); // Will add element again to the list 
}

И мы можем вызвать этот метод, чтобы добавить элемент в наш список

List<String> list = new ArrayList<>();

addToList(list, "one");
addToList(list, "two");
addToList(list, "three");
addToList(list, "two");

Единственным недостатком здесь является вызов нашего пользовательского метода addToList() каждый раз вместо list.add()