Java中有序集的任何实现?

Posted

技术标签:

【中文标题】Java中有序集的任何实现?【英文标题】:Any implementation of Ordered Set in Java? 【发布时间】:2012-02-01 12:06:40 【问题描述】:

如果有人熟悉Objective-C,那么有一个名为NSOrderedSet 的集合充当Set,它的项可以作为Array 的项进行访问。

Java 中有这样的东西吗?

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

【问题讨论】:

我正在用 c++ 解决类似的问题。使用 NSOrderedSet,我们可以按照插入的顺序访问元素吗? 你知道如何在 C++ 中获得上述功能吗? i,e 充当 SET 并且可以作为 Array 的元素访问? 【参考方案1】:

indexed-tree-map 项目中的IndexedTreeSet 提供了此功能(按索引排序/排序集,具有类似列表的访问)。

【讨论】:

【参考方案2】:

看看LinkedHashSet类

From Java doc:

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

【讨论】:

非常感谢。看着LinkedHashMap 看起来很简单,但我还没有找到它。 Class LinkedHashSet<E>【参考方案3】:

我遇到了类似的问题。我不太需要一个有序的集合,但更多的是一个带有快速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(); 
    

【讨论】:

【参考方案4】:

看看Java standard API doc。在LinkedHashMap 旁边,有一个LinkedHashSet。但请注意,其中的顺序是插入顺序,而不是元素的自然顺序。而且您只能按该顺序进行迭代,而不能进行随机访问(通过计算迭代步数除外)。

还有一个接口SortedSetTreeSetConcurrentSkipListSet实现。两者都允许在其元素的natural order 或Comparator 中进行迭代,但不允许随机访问或插入顺序。

对于既可以通过索引有效访问又可以有效实现集合标准的数据结构,您需要一个skip list,但在 Java 标准 API 中没有实现该功能,尽管我我确信在互联网上很容易找到。

【讨论】:

我可能误解了您的评论,但我的印象是自 Java 1.6 以来有几个基于跳过列表的默认集合(例如,ConcurrentSkipListSet 等)。 @user988052: 是的,但是那些没有通过索引实现随机访问(尽管我对跳过列表的理解说这应该是可能的),这似乎是 Uko 想要的。 @MichaelBorgwardt Java 6 及更高版本包括一对跳过列表实现:ConcurrentSkipListMapConcurrentSkipListSet。两者都维护基于自然顺序或比较器的排序。我不明白他们是否提供您讨论的随机访问或进入顺序。 @BasilBourque:很好的发现,感谢您的编辑。 OP 想要通过索引访问,现在我已经查看并考虑了它,我认为跳过列表实际上也没有这种能力......【参考方案5】:

尝试使用实现SortedSetjava.util.TreeSet

引用文档:

“元素使用它们的自然顺序进行排序,或者通过在集合创建时提供的 Comparator 进行排序,具体取决于使用的构造函数”

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

如果您想以数组的形式访问集合的内容,您可以将其转换为:

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

该数组将使用与 TreeSet 相同的标准(自然或通过比较器)进行排序,并且在许多情况下,这将比使用 Arrays.sort() 更有优势

【讨论】:

我需要像在 ArrayList e.i.如果我放置第一个元素c,然后放置元素a,当我迭代一个集合时,我想以相同的顺序获取它们:ca 等。【参考方案6】:

您还可以从双向地图中获得一些实用程序,例如来自Google Guava 的BiMap

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

【讨论】:

【参考方案7】:

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

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

另外,你不能在某个位置插入。

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

【讨论】:

我希望能够在特定位置设置/获取元素并按添加它们的顺序获取它们。看来LinkedHashSet 应该这样做。感谢回复【参考方案8】:

如果我们谈论的是跳过列表的廉价实现,我想知道在大 O 方面,这个操作的成本是多少:

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

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

java.util.Arrays#copyOf

【讨论】:

这取决于迭代器的性能特征和底层集合的size()方法。迭代通常是O(n),大小通常是O(1),除了ConcurrentSkipListSet,它是O(n)【参考方案9】:

treeset 是一个有序集,但您无法通过项目索引访问,只能遍历或转到开始/结束。

【讨论】:

使用 treeSet 会增加成本。 LinkedHashSet 成本更低。【参考方案10】:

TreeSet 已订购。

http://docs.oracle.com/javase/6/docs/api/java/util/TreeSet.html

【讨论】:

这是正确答案。与 LHSet 不同,TreeSet 确实 实现了 java.util.SortedSet. 有序和排序是不同的东西。 TreeSet 是有序的,不是有序的 确切地说,ordered 是指插入顺序(List 的工作方式),而 sorted 是指基于某些标准的元素的事后排序。

以上是关于Java中有序集的任何实现?的主要内容,如果未能解决你的问题,请参考以下文章

java实现二分查找(迭代与递归)

java实现二分查找(迭代与递归)

并查集的Java实现

插入排序算法--Java实现

有序线性表(存储结构数组)--Java实现

Java 实现有序链表