Есть ли реализация списка без дублирования?


86

Знаю SortedSet, но в моем случае мне нужно то, что реализует List, а нет Set. Так есть ли там реализация, в API или где-то еще?

Это не должно быть сложно реализовать, но я подумал, почему бы сначала не спросить здесь людей?


1
Зачем нужно реализовать List? Наборы итерируемы, как и списки, поэтому я полагаю, что метод получения применяет List по какой-то другой причине.
Роб

@Rob Верно, это внешнее требование, а структура данных включает намного больше, чем один List.
Yuval

Если пользователю нужен СПИСОК, то очевидно, что ему нужны методы интерфейса СПИСОК, которых нет в интерфейсе SET ...
marcolopes

Ответы:


92

Для этого в стандартной библиотеке нет коллекции Java. однако LinkedHashSet<E>сохраняет порядок, аналогично a List, поэтому, если вы оберните свой набор в a, Listкогда вы хотите использовать его как a, Listвы получите желаемую семантику.

В качестве альтернативы, Коллекции Commons (или commons-collections4для общей версии) имеют, Listкоторый уже делает то, что вы хотите: SetUniqueList/ SetUniqueList<E>.


5
Класс Commons - это именно то, что мне нужно, но мой босс посоветовал мне реализовать его сам. В любом случае в 10 раз!
Yuval

5
Нет ничего лучше, чем изобретать велосипед! Теперь вы все равно будете знать, возникнет ли такая необходимость. collection15 - довольно полезная вещь, которую можно повозить; MultiMaps, в частности, облегчает боль от чего-то, что в конечном итоге приходится много реализовывать.
Calum

19
@skaffman: на самом деле он не идиот, но иногда он делает ... ну, странные ходы. В любом случае, я не буду вносить в продукт ошибки. На сегодняшнем рынке я доволен своей работой и не хочу захлопывать двери и сжигать мосты, если вы понимаете мою точку зрения.
Yuval

3
Я очень удивлен, когда SetUniqueList не имеет параметризованного типа.
emeraldhieu

2
Джеффри: На мобильных платформах система обычно удаляет неиспользуемые классы, но, конечно, есть множество причин, по которым вы не можете отказаться от одного из этих «нормальных» решений. Всегда нужно идти на компромисс, и никакое решение не устранит все случаи.
Calum

14

Вот что я сделал, и это работает.

Предполагая, что мне нужно ArrayListработать, первое, что я сделал, это создал новый LinkedHashMap.

LinkedHashSet<E> hashSet = new LinkedHashSet<E>()

Затем я пытаюсь добавить новый элемент в LinkedHashSet. Метод add не изменяет LinkedHasSetи возвращает false, если новый элемент является дубликатом. Это становится условием, которое я могу протестировать перед добавлением в ArrayList.

if (hashSet.add(E)) arrayList.add(E);

Это простой и элегантный способ предотвратить добавление дубликатов в список массивов. Если вы хотите, вы можете инкапсулировать его и переопределить метод add в классе, который расширяет ArrayList. Просто не забывайте работать с addAllциклом по элементам и вызывать метод добавления.


1
Да, я думаю, это лучшее решение для этого, вы также можете просто использовать обычный HashSet, а не Linked, а затем вы можете использовать свой список по своему усмотрению, вы также можете решить, что делать в некоторых ситуациях, например, в добавляя элемент внутри списка перед определенным индексом, вы можете решить, хотите ли вы переместить дублированный элемент в эту позицию или нет.
gyurix

Лучшее решение здесь ... Опубликую мой код класса
UniqueList

Это сработало для меня в моем алгоритме BFS Graph. Потому что у меня было несколько узлов, которые я добавил в очередь (LinkedList), если они еще не были в нем.
Жанкарло Фонтальво

11

Итак, вот что я в итоге сделал. Надеюсь, это поможет кому-то другому.

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);
        }
    }
}   

10
Будьте осторожны - LinkedList.contains () необходимо просканировать весь список, чтобы определить, содержится ли объект в нем. Это означает, что когда вы добавляете объекты в большой список, весь список сканируется для каждой операции добавления (в худшем случае). Это может закончиться МЕДЛЕННО.
matt b

8
Кроме того, переопределение addAll не проверяет наличие дубликатов в коллекции, передаваемой в addAll ().
matt b

@mattb Как бы вы тогда решили эту проблему: на Android при привязке объектов к представлению элемента списка нам дается позиция элемента в адаптере представления. Поскольку наборы не имеют индекса, единственный способ проверить, существует ли объект при использовании списков, - это выполнить итерацию и найти существующую копию.
TheRealChx101 05

6

Почему бы не инкапсулировать набор списком, например:

new ArrayList( new LinkedHashSet() )

Остается другая реализация для тех, кто является настоящим мастером коллекций ;-)


4
Этот конструктор копирует содержимое Set в новый List, а не упаковывает его.
Calum

@Calum, это правильно, но вместо того, чтобы беспокоиться о том, чтобы не добавлять дубликаты в список, он может добавить свои объекты в набор (и позволить Set беспокоиться о фильтрации дубликатов) и просто обернуть этот набор в список при передаче его в внешний метод.
matt b

4
Это копирует набор в список, но у вас нет хорошо известного порядка. Но в этом весь вопрос.
Janning

4

Вам следует серьезно обдумать ответ Дхиллера:

  1. Вместо того, чтобы беспокоиться о добавлении ваших объектов в список без дубликатов, добавьте их в Set (любая реализация), который по своей природе отфильтрует дубликаты.
  2. Когда вам нужно вызвать метод, требующий List, оберните его в new ArrayList(set)(или в new LinkedList(set)любой другой).

Я думаю, что решение, которое вы опубликовали с помощью, NoDuplicatesListимеет некоторые проблемы, в основном с contains()методом, плюс ваш класс не обрабатывает проверку дубликатов в коллекции, переданной вашему addAll()методу.


Я хотел бы узнать об этих проблемах contains (). Что касается addAll (), я создаю копию данной коллекции и удаляю все объекты, уже находящиеся в this. Как это не обрабатывать дубликаты?
Yuval

Как я упоминал в своем комментарии к вашей публикации класса, contains () должен сканировать весь список (в худшем случае), чтобы определить, содержится ли объект в списке. Если у вас есть список из 1 миллиона элементов, и вы добавляете его по отдельности, то (в худшем случае) сканируется более десяти миллионов элементов.
matt b

Что касается addAll (), если Коллекция, переданная в addAll, сама содержит дубликаты, они не обнаруживаются. Например: ваш список {A, B, C, D} список параметров {B, D, E, E, E}. Вы создаете копию параметра, и после removeAll она содержит {E, E, E}.
matt b

Проблема addAll () на самом деле не имеет для меня отношения, поскольку я использую NoDuplicatesList на протяжении всей процедуры, а addAll () должен получать еще один NoDuplicatesList в качестве своего параметра. Что бы вы посоветовали улучшить производительность contains ()?
Yuval

3

Мне нужно было что-то подобное, поэтому я пошел в общие коллекции и использовал SetUniqueList, но когда я провел некоторый тест производительности, я обнаружил, что он не оптимизирован по сравнению со случаем, если я хочу использовать Setи получить с Arrayпомощью Set.toArray()метода.

Для заполнения и последующего прохождения 100000 строкSetUniqueTest потребовалось 20: 1 времени по сравнению с другой реализацией, что является большой разницей.

Итак, если вы беспокоитесь о производительности, я рекомендую вам использовать Set и Get a Array вместо использования 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

}

С уважением, Мохаммед Слим


1

ПРИМЕЧАНИЕ: он не принимает во внимание реализацию подсписка .

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));
    }

}

0

В документации для интерфейсов сбора говорится:

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

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


Я специально упомянул, что мне нужна реализация List. Поверьте, есть причина.
Yuval

Причина в том, что вы взаимодействуете с API, который принимает список в качестве параметра (вместо коллекции)? С этим немного неприятно иметь дело
мэтт б

На самом деле API принимает Map <AccountType, Map <AccountType, List <Account> >>, что означает хранение где-то от десятков до сотен списков ...
Yuval

При построении функций вероятности с парами элемент-вероятность не должно быть дубликатов, хотя повторяющиеся элементы можно просто объединить.
Эл Дж. Джонстон,

-1

in add, почему бы не использовать HashSet.add()для проверки дубликатов вместо HashSet.consist(). HashSet.add()вернется, trueесли нет дубликатов и в falseпротивном случае.


Что есть HashSet#consist()?
naXa

-1

Вне моей головы списки допускают дублирование. Вы можете быстро реализовать UniqueArrayListи переопределить все функции add/ insertдля проверки contains()перед вызовом унаследованных методов. Для личного использования вы можете реализовать только тот addметод, который используете, и переопределить другие, чтобы генерировать исключение на случай, если будущие программисты попытаются использовать список по-другому.


Я был готов вернуться к этой идее (что в конечном итоге мне пришлось), если никто не предложил ничего лучше = 8-) См. Мой собственный ответ выше.
Yuval

-3

Я только что создал свой собственный список 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.
gyurix
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.