LinkedHashSet - порядок вставки и дубликаты - сохраняйте новейшие "сверху",
Мне нужна коллекция, которая сохраняет порядок вставки и имеет уникальные значения. LinkedHashSet выглядит как способ, но есть одна проблема - когда два элемента равны, он удаляет самую новую (что имеет смысл), вот пример:
set.add("one");
set.add("two");
set.add("three");
set.add("two");
LinkedHashSet
напечатает:
one
, two
, three
Но мне нужно:
one
, three
, two
Что было бы лучшим решением здесь? Есть ли способ сбора/коллекций, который может это сделать или я должен выполнить его вручную?
Ответы
Ответ 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()