Я знаю о SortedSet, но в моем случае мне нужно что-то, что реализует List, а не Set. Так есть ли там реализация, в API или где-то еще?
Реализовать себя должно быть несложно, но я подумал, почему бы сначала не спросить здесь людей?
@Rob Правильно, это внешнее требование, и структура данных включает намного больше, чем один List.
Если пользователю нужен СПИСОК, то ясно, что ему нужны методы интерфейса СПИСОК, которых нет в интерфейсе SET ...




Почему бы не инкапсулировать набор списком, например:
new ArrayList( new LinkedHashSet() )
Остается другая реализация для тех, кто является настоящим мастером коллекций ;-)
Этот конструктор копирует содержимое Set в новый список, а не упаковывает его.
@Calum, это правильно, но вместо того, чтобы беспокоиться о том, чтобы не добавлять дубликаты в список, он может добавить свои объекты в набор (и позволить Set беспокоиться о фильтрации дубликатов) и просто обернуть этот набор в список при передаче его в внешний метод.
Это копирует набор в список, но у вас нет хорошо известного порядка. Но в этом весь вопрос.
документация для интерфейсов коллекций говорит:
Set — a collection that cannot contain duplicate elements.
List — an ordered collection (sometimes called a sequence). Lists can contain duplicate elements.
Поэтому, если вам не нужны дубликаты, вам, вероятно, не следует использовать список.
Я специально упомянул, что мне нужна реализация List. Поверьте мне, есть причина.
Причина в том, что вы взаимодействуете с API, который принимает список в качестве параметра (вместо коллекции)? Это немного раздражает, когда приходится иметь дело с
На самом деле API принимает Map <AccountType, Map <AccountType, List <Account> >>, что означает хранение где-то от десятков до сотен списков ...
При построении функций вероятности с парами элемент-вероятность не должно быть дубликатов, хотя повторяющиеся элементы можно просто объединить.
Для этого в стандартной библиотеке нет коллекции Java. Однако LinkedHashSet<E> сохраняет порядок так же, как и List, поэтому, если вы обернете свой набор в List, когда вы хотите использовать его как List, вы получите желаемую семантику.
В качестве альтернативы, Коллекции Commons (или commons-collections4 для общей версии) имеет List, который уже делает то, что вы хотите: SetUniqueList / SetUniqueList<E>.
Класс Commons - это именно то, что мне нужно, но мой босс посоветовал мне реализовать его сам. В любом случае в 10 раз!
Нет ничего лучше, чем изобретать велосипед! Теперь вы все равно узнаете, возникнет ли такая необходимость. collection15 - довольно полезная вещь, которую можно повозить; MultiMaps, в частности, облегчает боль от чего-то, что в конечном итоге приходится много реализовывать.
@skaffman: на самом деле он не идиот, но иногда он делает ... ну, странные ходы. В любом случае, я не буду вносить в продукт ошибки. На сегодняшнем рынке я доволен своей работой и не хочу хлопать дверями и сжигать мосты, если вы понимаете мою точку зрения.
Я очень удивлен, когда SetUniqueList не имеет параметризованного типа.
упаковка LinkedHashSet в список не упрощает и не упрощает реализацию всех операций на основе индекса со списком. я не думаю, что это разумный подход. а также есть причина не использовать обычные вещи, например, на мобильной платформе, где размер имеет значение.
Джеффри: На мобильных платформах система обычно удаляет неиспользуемые классы, но, конечно, есть множество причин, по которым вы не можете отказаться от одного из этих «нормальных» решений. Всегда нужно идти на компромисс, и никакое решение не устранит все случаи.
Просто имейте в виду, сортировка НЕ будет работать в этом "списке".
Вне моей головы списки допускают дублирование. Вы можете быстро реализовать UniqueArrayList и переопределить все функции add / insert для проверки наличия contains() перед вызовом унаследованных методов. Для личного использования вы можете реализовать только тот метод add, который вы используете, и переопределить другие, чтобы генерировать исключение на случай, если будущие программисты попытаются использовать список по-другому.
Я был готов вернуться к этой идее (что в конечном итоге мне пришлось), если никто не предложил ничего лучше = 8-) См. Мой собственный ответ выше.
Итак, вот что я в итоге сделал. Надеюсь, это поможет кому-то другому.
class NoDuplicatesList<E> extends LinkedList<E> {
@Override
public boolean add(E e) {
if (this.contains(e)) {
return false;
}
else {
return super.add(e);
}
}
@Override
public boolean addAll(Collection<? extends E> collection) {
Collection<E> copy = new LinkedList<E>(collection);
copy.removeAll(this);
return super.addAll(copy);
}
@Override
public boolean addAll(int index, Collection<? extends E> collection) {
Collection<E> copy = new LinkedList<E>(collection);
copy.removeAll(this);
return super.addAll(index, copy);
}
@Override
public void add(int index, E element) {
if (this.contains(element)) {
return;
}
else {
super.add(index, element);
}
}
}
Будьте осторожны - LinkedList.contains () необходимо просканировать весь список, чтобы определить, содержится ли объект в нем. Это означает, что когда вы добавляете объекты в большой список, весь список сканируется для каждой операции добавления (в худшем случае). Это может закончиться МЕДЛЕННО.
Кроме того, ваше переопределение addAll не проверяет наличие дубликатов в коллекции, передаваемой в addAll ().
@mattb Как бы вы тогда решили эту проблему: на Android при привязке объектов к представлению элемента списка нам дается позиция элемента в адаптере представления. Поскольку наборы не имеют индекса, единственный способ проверить, существует ли объект при использовании списков, - это выполнить итерацию и найти существующую копию.
Вам следует серьезно обдумать ответ Дхиллера:
new ArrayList(set) (или new LinkedList(set), что угодно).Я думаю, что решение, которое вы опубликовали с NoDuplicatesList, имеет некоторые проблемы, в основном с методом contains(), плюс ваш класс не обрабатывает проверку дубликатов в Коллекции, переданной вашему методу addAll().
Я хотел бы узнать об этих проблемах с contains (). Что касается addAll (), я создаю копию данной коллекции и удаляю все объекты, уже находящиеся в this. Как это не обрабатывать дубликаты?
Как я уже упоминал в своем комментарии к вашей публикации класса, contains () должен сканировать весь список (в худшем случае), чтобы определить, содержится ли объект в списке. Если у вас есть список из 1 миллиона элементов, и вы добавляете его по отдельности, то (в худшем случае) сканируется более десяти миллионов элементов.
Что касается addAll (), если Коллекция, переданная в addAll, сама содержит дубликаты, они не обнаруживаются. Например: ваш список {A, B, C, D} список параметров {B, D, E, E, E}. Вы создаете копию параметра, и после removeAll она содержит {E, E, E}.
Проблема addAll () на самом деле для меня не имеет отношения, поскольку я использую NoDuplicatesList на протяжении всей процедуры, а addAll () должен получать еще один NoDuplicatesList в качестве своего параметра. Что бы вы посоветовали улучшить производительность contains ()?
Мне нужно было что-то подобное, поэтому я пошел к общие коллекции и использовал SetUniqueList, но когда я провел какой-то тест производительности, я обнаружил, что он не оптимизирован по сравнению со случаем, если я хочу использовать Set и получить Array с помощью Set.toArray() метод.
SetUniqueTest потребовал 20: 1 раз для заполнения, а затем прохождения 100000 струн по сравнению с другой реализацией, что является большой разницей.
Итак, если вы беспокоитесь о производительности, я рекомендую вам использовать Установить и получить массив вместо использования SetUniqueList, если вам действительно не нужна логика SetUniqueList, тогда вам нужно будет проверить другие решения ...
Основной метод тестирования кода:
public static void main(String[] args) {
SetUniqueList pq = SetUniqueList.decorate(new ArrayList());
Set s = new TreeSet();
long t1 = 0L;
long t2 = 0L;
String t;
t1 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
pq.add("a" + Math.random());
}
while (!pq.isEmpty()) {
t = (String) pq.remove(0);
}
t1 = System.nanoTime() - t1;
t2 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
s.add("a" + Math.random());
}
s.clear();
String[] d = (String[]) s.toArray(new String[0]);
s.clear();
for (int i = 0; i < d.length; i++) {
t = d[i];
}
t2 = System.nanoTime() - t2;
System.out.println((double)t1/1000/1000/1000); //seconds
System.out.println((double)t2/1000/1000/1000); //seconds
System.out.println(((double) t1) / t2); //comparing results
}
С уважением, Мохаммед Слим
Я только что создал свой собственный список UniqueList в своей небольшой библиотеке, вот так:
package com.bprog.collections;//my own little set of useful utilities and classes
import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Jonathan
*/
public class UniqueList {
private HashSet masterSet = new HashSet();
private ArrayList growableUniques;
private Object[] returnable;
public UniqueList() {
growableUniques = new ArrayList();
}
public UniqueList(int size) {
growableUniques = new ArrayList(size);
}
public void add(Object thing) {
if (!masterSet.contains(thing)) {
masterSet.add(thing);
growableUniques.add(thing);
}
}
/**
* Casts to an ArrayList of unique values
* @return
*/
public List getList(){
return growableUniques;
}
public Object get(int index) {
return growableUniques.get(index);
}
public Object[] toObjectArray() {
int size = growableUniques.size();
returnable = new Object[size];
for (int i = 0; i < size; i++) {
returnable[i] = growableUniques.get(i);
}
return returnable;
}
}
У меня есть класс TestCollections, который выглядит так:
package com.bprog.collections;
import com.bprog.out.Out;
/**
*
* @author Jonathan
*/
public class TestCollections {
public static void main(String[] args){
UniqueList ul = new UniqueList();
ul.add("Test");
ul.add("Test");
ul.add("Not a copy");
ul.add("Test");
//should only contain two things
Object[] content = ul.toObjectArray();
Out.pl("Array Content",content);
}
}
Работает отлично. Все, что он делает, - это добавляет к набору, если его еще нет, и есть возвращаемый Arraylist, а также массив объектов.
Да, вам следует добавить к нему еще немного методов для реализации интерфейса List.
в методе add, почему бы не использовать HashSet.add() для проверки дубликатов вместо HashSet.consist().
HashSet.add() вернет true, если нет дубликатов, и false в противном случае.
Что такое HashSet#consist()?
Вот что я сделал, и это работает.
Предполагая, что у меня есть ArrayList для работы, первое, что я сделал, - это новый LinkedHashMap.
LinkedHashSet<E> hashSet = new LinkedHashSet<E>()
Затем я пытаюсь добавить свой новый элемент в LinkedHashSet. Метод add не изменяет LinkedHasSet и возвращает false, если новый элемент является дубликатом. Это становится условием, которое я могу проверить перед добавлением в ArrayList.
if (hashSet.add(E)) arrayList.add(E);
Это простой и элегантный способ предотвратить добавление дубликатов в список массивов. Если вы хотите, вы можете инкапсулировать его и переопределить метод добавления в классе, расширяющем ArrayList. Просто не забудьте иметь дело с addAll, перебирая элементы и вызывая метод добавления.
Да, я думаю, это лучшее решение для этого, вы также можете просто использовать обычный HashSet, а не Linked, а затем вы можете использовать свой список по своему усмотрению, вы также можете решить, что делать в некоторых ситуациях, например, в добавляя элемент внутри списка перед определенным индексом, вы можете решить, хотите ли вы переместить дублированный элемент в эту позицию или нет.
Лучшее решение здесь ... Опубликую мой код класса UniqueList
Это сработало для меня в моем алгоритме BFS Graph. Потому что у меня было несколько узлов, которые я добавил в очередь (LinkedList), если их еще не было.
ПРИМЕЧАНИЕ: он не принимает во внимание реализацию подсписок.
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class UniqueList<T> extends ArrayList<T> {
private static final long serialVersionUID = 1L;
/** Unique elements SET */
private final Set<T> set=new HashSet();
/** Used by addAll methods */
private Collection<T> addUnique(Collection<? extends T> col) {
Collection<T> unique=new ArrayList();
for(T e: col){
if (set.add(e)) unique.add(e);
}
return unique;
}
@Override
public boolean add(T e) {
return set.add(e) ? super.add(e) : false;
}
@Override
public boolean addAll(Collection<? extends T> col) {
return super.addAll(addUnique(col));
}
@Override
public void add(int index, T e) {
if (set.add(e)) super.add(index, e);
}
@Override
public boolean addAll(int index, Collection<? extends T> col) {
return super.addAll(index, addUnique(col));
}
}
Зачем нужно внедрять List? Наборы итерируемы, как и списки, поэтому я полагаю, что метод получения применяет List по какой-то другой причине.