Любая реализация упорядоченного набора в Java? - PullRequest
86 голосов
/ 03 января 2012

Если кто-то знаком с Objective-C, существует коллекция под названием NSOrderedSet, которая действует как Set , и к ее элементам можно обращаться как Array Вот такие.

Есть ли что-нибудь подобное в Java?

Я слышал, что есть коллекция под названием LinkedHashMap, но я ничего не нашелнравится это для набора.

Ответы [ 10 ]

103 голосов
/ 03 января 2012

Взгляните на LinkedHashSet класс

30 голосов
/ 03 января 2012

В каждом наборе есть итератор (). Обычный итератор HashSet довольно случайный, TreeSet делает это в порядке сортировки, LinkedHashSet итератор выполняет итерацию в порядке вставки.

Однако вы не можете заменить элемент в LinkedHashSet. Вы можете удалить один и добавить другой, но новый элемент не будет на месте оригинала. В LinkedHashMap вы можете заменить значение для существующего ключа, и тогда значения будут по-прежнему в исходном порядке.

Кроме того, вы не можете вставить в определенную позицию.

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

10 голосов
/ 03 января 2012

Взгляните на стандарт Java API doc .Прямо рядом с LinkedHashMap есть LinkedHashSet.Но обратите внимание, что порядок в них - это порядок вставки, а не естественный порядок элементов.И вы можете выполнять итерацию только в этом порядке, но не выполнять произвольный доступ (за исключением подсчета шагов итерации).

Существует также интерфейс SortedSet, реализованный TreeSet и ConcurrentSkipListSet.Оба допускают итерацию в естественном порядке их элементов или Comparator, но не в произвольном порядке или в порядке вставки.

Для структуры данных, котораяимеет эффективный доступ по индексу и может эффективно реализовывать заданный критерий, вам потребуется список пропуска , но в Java Standard API нет реализации с такой функциональностью, хотя я уверен, что ее легко найтиодин в интернете.

4 голосов
/ 03 января 2012

Попробуйте использовать java.util.TreeSet, который реализует SortedSet.

Чтобы процитировать документ:

"Элементыупорядочено с использованием их естественного упорядочения или с помощью компаратора, предоставленного во время создания набора, в зависимости от того, какой конструктор используется "

Обратите внимание, что для добавления, удаления и размещения имеется журнал затрат времени (n).

Если вы хотите получить доступ к содержимому набора в виде массива, вы можете преобразовать его следующим образом:

YourType[] array = someSet.toArray(new YourType[yourSet.size()]); 

Этот массив будет отсортирован по тем же критериям, что и TreeSet (натуральный иликомпаратором), и во многих случаях это будет иметь преимущество вместо Arrays.sort ()

4 голосов
/ 03 января 2012
1 голос
/ 03 января 2012

treeset - упорядоченный набор, но вы не можете получить доступ через индекс элементов, просто переберите или перейдите к началу / концу.

0 голосов
/ 02 ноября 2016

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

отказ от ответственности: не проверено

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.Collection;
import java.util.Comparator;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import static java.util.Objects.requireNonNull;

/**
 * An ArrayList that keeps an index of its content so that contains()/indexOf() are fast. Duplicate entries are
 * ignored as most other java Set's do.
 */
public class IndexedArraySet<E> extends ArrayList<E> implements Set<E> {

    public IndexedArraySet() { super(); }

    public IndexedArraySet(Iterable<E> c) {
        super();
        addAll(c);
    }

    private HashMap<E, Integer> indexMap = new HashMap<>();

    private void reindex() {
        indexMap.clear();
        int idx = 0;
        for (E item: this) {
            addToIndex(item, idx++);
        }
    }

    private E addToIndex(E e, int idx) {
        indexMap.putIfAbsent(requireNonNull(e), idx);
        return e;
    }

    @Override
    public boolean add(E e) {
        if(indexMap.putIfAbsent(requireNonNull(e), size()) != null) return false;
        super.add(e);
        return true;
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        return addAll((Iterable<? extends E>) c);
    }
    public boolean addAll(Iterable<? extends E> c) {
        boolean rv = false;
        for (E item: c) {
            rv |= add(item);
        }
        return rv;
    }

    @Override
    public boolean contains(Object e) {
        return indexMap.containsKey(e);
    }

    @Override

    public int indexOf(Object e) {
        if (e == null) return -1;
        Integer i = indexMap.get(e);
        return (i == null) ? -1 : i;
    }

    @Override
    public int lastIndexOf(Object e) {
        return indexOf(e);
    }

    @Override @SuppressWarnings("unchecked")
    public Object clone() {
        IndexedArraySet clone = (IndexedArraySet) super.clone();
        clone.indexMap = (HashMap) indexMap.clone();
        return clone;
    }

    @Override
    public void add(int idx, E e) {
        if(indexMap.putIfAbsent(requireNonNull(e), -1) != null) return;
        super.add(idx, e);
        reindex();
    }

    @Override
    public boolean remove(Object e) {
        boolean rv;
        try { rv = super.remove(e); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public void clear() {
        super.clear();
        indexMap.clear();
    }

    @Override
    public boolean addAll(int idx, Collection<? extends E> c) {
        boolean rv;
        try {
            for(E item : c) {
                // check uniqueness
                addToIndex(item, -1);
            }
            rv = super.addAll(idx, c);
        } finally {
            reindex();
        }
        return rv;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        boolean rv;
        try { rv = super.removeAll(c); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        boolean rv;
        try { rv = super.retainAll(c); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public boolean removeIf(Predicate<? super E> filter) {
        boolean rv;
        try { rv = super.removeIf(filter); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public void replaceAll(final UnaryOperator<E> operator) {
        indexMap.clear();
        try {
            int duplicates = 0;
            for (int i = 0; i < size(); i++) {
                E newval = requireNonNull(operator.apply(this.get(i)));
                if(indexMap.putIfAbsent(newval, i-duplicates) == null) {
                    super.set(i-duplicates, newval);
                } else {
                    duplicates++;
                }
            }
            removeRange(size()-duplicates, size());
        } catch (Exception ex) {
            // If there's an exception the indexMap will be inconsistent
            reindex();
            throw ex;
        }

    }

    @Override
    public void sort(Comparator<? super E> c) {
        try { super.sort(c); }
        finally { reindex(); }
    }
}
0 голосов
/ 08 января 2015

Вы также можете получить некоторую полезность из двунаправленной карты, например BiMap из Google Guava

С BiMap вы можете довольно эффективно отобразить целое число (для произвольного доступа к индексу) на любой другой тип объекта. BiMap s являются взаимно-однозначными, поэтому любое заданное целое число имеет не более одного элемента, связанного с ним, а любой элемент имеет одно связанное целое число. Он ловко подкреплен двумя HashTable экземплярами, поэтому он использует почти вдвое больше памяти, но он намного эффективнее, чем пользовательский List, в отношении обработки, поскольку contains() (который вызывается при добавлении элемента для проверки он уже существует) - это операция с постоянным временем и параллельная работа, подобная HashSet, в то время как реализация List НАМНОГО медленнее.

0 голосов
/ 27 января 2014

IndexedTreeSet из проекта indexed-tree-map предоставляет эту функциональность (упорядоченный / отсортированный набор со списком доступа по индексу).

0 голосов
/ 23 октября 2012

Если мы говорим о недорогой реализации пропускаемого списка, мне интересно, если говорить о больших O, какова стоимость этой операции:

YourType [] array = someSet.toArray (новый YourType [yourSet.size ()]);

Я имею в виду, что это всегда застревает в создании целого массива, поэтому это O (n):

java.util.Arrays#copyOf
...