Чи існує там не дубльована реалізація Списку?


86

Я знаю про SortedSet , але в моєму випадку мені потрібно щось, що реалізує List, і ні Set. То чи є там реалізація, в API чи деінде?

Це не повинно бути важко реалізувати себе, але я зрозумів, чому б не спершу запитати людей тут?


1
Навіщо потрібно застосовувати List? Набори є ітерабельними, як списки, тому я вважаю, що метод отримання застосовує List з якихось інших причин.
Роб

@Rob Правильно, це зовнішній попит, а структура даних включає в себе набагато більше одного списку.
Юваль

Якщо користувач хоче СПИСОК, то зрозуміло, що потрібні методи інтерфейсу СПИСОК, яких немає в інтерфейсі SET ...
marcolopes

Відповіді:


92

У стандартній бібліотеці для цього немає колекції Java. LinkedHashSet<E>зберігає порядок подібно до List, однак, якщо ви обернете свій набір у, Listколи ви хочете використовувати його в якості, Listви отримаєте потрібну семантику.

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


5
Клас Commons - це саме те, що мені потрібно, але мій начальник сказав мені, щоб я його впровадив сам. Все одно в 10 разів!
Юваль

5
Ну добре, нічого, як винахід колеса! Зараз ви все одно дізнаєтесь, якщо потреба з’явиться знову. колекції15 - це досить корисна річ, щоб розмиватися; MultiMaps, зокрема, полегшує біль від того, що в кінцевому підсумку дуже багато реалізує себе.
Калум

19
@skaffman: насправді він не ідіот, але іноді він робить ходи, які ... ну, дивні. У будь-якому випадку, я не збираюся вводити помилки в продукт. На сучасному ринку я задоволений своєю роботою і не хочу грюкати двері та спалювати мости, якщо ви зрозумієте мою думку.
Юваль

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, переглядаючи елементи та викликаючи метод add.


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

6

Чому б не інкапсулювати набір зі списком, сортуючи так:

new ArrayList( new LinkedHashSet() )

Це залишає іншу реалізацію для тих, хто є справжнім майстром колекцій ;-)


4
Цей конструктор копіює вміст набору до нового списку, а не обертає його.
Калум

@Calum, це правильно, але замість того, щоб турбуватися про не додавання дублікатів до Списку, він може додавати свої об'єкти до Набору (і нехай Набір турбується про фільтрацію дублікатів) і просто обертає цей Набір у Списку, передаючи його зовнішній метод.
matt b

4
Це копіює набір у список, але у вас немає жодного відомого замовлення. Але це те, про що питання.
Janning

4

Вам слід серйозно розглянути відповідь Дхіллера:

  1. Замість того, щоб турбуватися про додавання об’єктів до списку без дублікатів, додайте їх до набору (будь-яка реалізація), який за своєю природою відфільтрує дублікати.
  2. Коли вам потрібно викликати метод, який вимагає List, оберніть його в new ArrayList(set)(або a new LinkedList(set), що завгодно).

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


Я хотів би дізнатись про ці матеріали, що містять (). Що стосується addAll (), то я створюю копію даної колекції і видаляю всі об’єкти, що вже є в 'this'. Як це не обробляє дублікати?
Юваль

Як я вже згадував у своєму коментарі до публікації вашого класу, contains () повинен відсканувати весь список (у гіршому випадку), щоб визначити, чи об’єкт міститься у списку. Якщо у вас є список з 1 мільйона предметів і ви додаєте 10 його окремо, тоді (у гіршому випадку) сканується понад десять мільйонів предметів.
matt b

Що стосується addAll (), якщо колекція, передана addAll, містить самі дублікати, вони не виявляються. Наприклад: ваш список {A, B, C, D} список параметрів {B, D, E, E, E}. Ви створюєте копію параметра, а після видаленняВсе він містить {E, E, E}.
matt b

Проблема з addAll () для мене насправді не актуальна, оскільки я використовую NoDuplicatesList протягом усієї процедури, і addAll () повинен отримувати інший NoDuplicatesList як свій параметр. Що б ви запропонували для покращення продуктивності contains ()?
Юваль

3

Мені потрібно було щось подібне, тому я пішов до колекцій спільних книг і використав файлSetUniqueList , але, провівши певний тест продуктивності, я виявив, що це, здається, не оптимізовано порівняно з випадком, якщо я хочу використовувати a Setта отримати метод Arrayвикористання Set.toArray().

Потрібно SetUniqueTestбуло 20: 1 заповнити, а потім пройти 100 000 рядків порівняно з іншою реалізацією, що є великою різницею.

Отже, якщо ви турбуєтесь про продуктивність, я рекомендую вам використовувати Set та Get 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

У документації для інтерфейсів колекції сказано:

Набір - колекція, яка не може містити повторюваних елементів.
Список - упорядкована колекція (іноді її називають послідовністю). Списки можуть містити повторювані елементи.

Тож якщо ви не хочете дублікатів, вам, мабуть, не слід використовувати список.


Я спеціально згадав, що мені потрібна реалізація List. Повірте мені, є причина.
Юваль

Причина в тому, що ви взаємодієте з API, який приймає список як параметр (замість колекції)? Це трохи дратує, коли доводиться мати справу
matt b

Насправді API бере Map <AccountType, Map <AccountType, List <Account> >>, що означає зберігання десь в районі десятків-сотень списків ... бах.
Юваль

Побудова імовірнісних функцій з парами елемент-ймовірність може включати не дублікати, хоча дублікати елементів можна просто об'єднати.
Al G Johnston,

-1

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


Що це таке HashSet#consist()?
naXa

-1

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


Я був готовий повернутися до цієї ідеї (що врешті-решт мені довелося), якщо ніхто не запропонує нічого кращого = 8-) Дивіться мою власну відповідь вище.
Юваль

-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.