Java中有序集的任何实现吗?


98

如果有人熟悉Objective-C中有一种叫做集合NSOrderedSet,它充当一个及其项目可以作为访问数组的人。

Java中有类似的东西吗?

我听说有一个名为的集合LinkedHashMap,但没有找到类似的集合。


我正在研究c ++中的类似问题。使用NSOrderedSet,我们可以按插入顺序访问元素吗?
Vinay 2014年

你知道如何在C ++中获得上述功能吗?即,充当SET并可以作为数组的元素来访问?
Vinay 2014年

Answers:


118

看看LinkedHashSet

从Java doc

Set接口的哈希表和链表实现,迭代顺序可预测。此实现与HashSet的不同之处在于,它维护一个贯穿其所有条目的双向链接列表。此链表定义了迭代顺序,即将元素插入集合中的顺序(插入顺序)请注意,如果将元素重新插入到set中,则插入顺序不会受到影响。(如果在调用之前s.contains(e)将返回true的情况下调用s.add(e),则将元素e重新插入到set s中。)


非常感谢你。看起来很琐碎,LinkedHashMap但我还没找到。
Uko 2012年


9
为什么这个答案获得如此多的赞誉?这根本不是问题的答案。没有任何功能LinkedHashSet可以让您确定元素所在的索引。
searchengine27年

31

每个Set都有一个iterator()。普通的HashSet的迭代器是相当随机的,TreeSet按排序顺序进行,LinkedHashSet迭代器按插入顺序进行迭代。

但是,您不能替换LinkedHashSet中的元素。您可以删除一个并添加另一个,但是新元素将不会代替原始元素。在LinkedHashMap中,您可以替换现有键的值,然后这些值仍将保持原始顺序。

另外,您不能在特定位置插入。

也许您最好使用带有明确检查的ArrayList以避免插入重复项。


我希望能够在特定位置上设置/获取元素,并按我添加的顺序获取它们。它接缝LinkedHashSet应该这样做。感谢您的回覆
Uko,2012年

11

看一下Java标准API文档。在旁边LinkedHashMap,有一个LinkedHashSet。但是请注意,这些顺序是插入顺序,而不是元素的自然顺序。而且,您只能按此顺序进行迭代,而不能进行随机访问(通过计算迭代步骤除外)。

还有一个SortedSetTreeSet和实现的接口ConcurrentSkipListSet。两者都允许以其元素的自然顺序或进行迭代Comparator,但不允许随机访问或插入顺序。

对于既可以通过索引进行有效访问又可以有效实现设置标准的数据结构,则需要一个跳过列表,但是Java Standard API中没有使用该功能的实现,尽管我敢肯定很容易找到一个在网上。


我可能会误解您的评论,但给人的印象是,自Java 1.6以来,有一些基于跳过列表的默认集合(例如ConcurrentSkipListSet等)。
TacticalCoder 2012年

@ user988052:是的,但是那些没有实现按索引的随机访问(尽管我对跳过列表的理解表明应该可行),这似乎是Uko想要的。
Michael Borgwardt 2012年

@MichaelBorgwardt Java 6和更高版本包括一对Skip List实现:ConcurrentSkipListMapConcurrentSkipListSet。两者都维护基于自然顺序或比较器的排序。我不知道他们是否提供您讨论的随机访问或进入顺序。
罗勒·布尔克

@BasilBourque:很好的发现,感谢您的修改。OP希望按索引进行访问,现在我已经对其进行了研究和思考,我认为跳过列表实际上也没有该功能……
Michael Borgwardt


5

尝试使用java.util.TreeSet该工具SortedSet

引用文档:

“根据元素的自然顺序或在集合创建时提供的Comparator来对元素进行排序,这取决于所使用的构造函数”

请注意,添加,删除和包含具有时间成本log(n)。

如果要以数组形式访问集合的内容,可以执行以下操作将其转换:

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

该数组将使用与TreeSet相同的条件进行排序(自然排序或通过比较器排序),在许多情况下,这比不执行Arrays.sort()有优势。


1
我需要订购像ArrayList的EI,如果我把第一个元素c,然后元素a,因为我遍历整个集合我想,让他们在同一顺序:ca等等
UKO


0

如果我们在谈论跳过列表的廉价实现,那么我想知道用大O表示此操作的成本是多少:

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

我的意思是它总是卡在整个数组的创建中,所以它是O(n):

java.util.Arrays#copyOf

1
这取决于迭代器的性能特征和size()基础集合的方法。迭代通常是O(n),大小通常O(1)除其ConcurrentSkipListSet位置外O(n)
伊恩·罗伯茨


0

你也可以得到一些实用了双向映射的,如BiMap谷歌番石榴

使用BiMap,您可以有效地将Integer(用于随机索引访问)映射到任何其他对象类型。BiMaps是一对一的,因此任何给定的整数最多具有一个与其关联的元素,并且任何元素均具有一个关联的整数。它由两个HashTable实例巧妙地支持,因此它使用几乎两倍的内存,但就处理而言,它比自定义要高效得多List,因为contains()(在添加项目以检查其是否存在时会被调用)是固定时间的和类似HashSet的并行友好操作,而List的实现要慢很多。


0

我有一个类似的问题。我不是很需要有序集合,而是更多带有快速indexOf/ 的列表contains。因为我什么都没找到,所以我自己实施了。这是代码,它同时实现SetList,尽管并非所有批量列表操作都和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(); }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.