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
的列表。因为我没有找到任何东西,所以我自己实现了一个。这是代码,它同时实现了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();
【讨论】:
【参考方案4】:看看Java standard API doc。在LinkedHashMap
旁边,有一个LinkedHashSet
。但请注意,其中的顺序是插入顺序,而不是元素的自然顺序。而且您只能按该顺序进行迭代,而不能进行随机访问(通过计算迭代步数除外)。
还有一个接口SortedSet
由TreeSet
和ConcurrentSkipListSet
实现。两者都允许在其元素的natural order 或Comparator
中进行迭代,但不允许随机访问或插入顺序。
对于既可以通过索引有效访问又可以有效实现集合标准的数据结构,您需要一个skip list,但在 Java 标准 API 中没有实现该功能,尽管我我确信在互联网上很容易找到。
【讨论】:
我可能误解了您的评论,但我的印象是自 Java 1.6 以来有几个基于跳过列表的默认集合(例如,ConcurrentSkipListSet 等)。 @user988052: 是的,但是那些没有通过索引实现随机访问(尽管我对跳过列表的理解说这应该是可能的),这似乎是 Uko 想要的。 @MichaelBorgwardt Java 6 及更高版本包括一对跳过列表实现:ConcurrentSkipListMap
和 ConcurrentSkipListSet
。两者都维护基于自然顺序或比较器的排序。我不明白他们是否提供您讨论的随机访问或进入顺序。
@BasilBourque:很好的发现,感谢您的编辑。 OP 想要通过索引访问,现在我已经查看并考虑了它,我认为跳过列表实际上也没有这种能力......【参考方案5】:
尝试使用实现SortedSet
的java.util.TreeSet
。
引用文档:
“元素使用它们的自然顺序进行排序,或者通过在集合创建时提供的 Comparator 进行排序,具体取决于使用的构造函数”
请注意,添加、删除和包含具有时间成本 log(n)。
如果您想以数组的形式访问集合的内容,您可以将其转换为:
YourType[] array = someSet.toArray(new YourType[yourSet.size()]);
该数组将使用与 TreeSet 相同的标准(自然或通过比较器)进行排序,并且在许多情况下,这将比使用 Arrays.sort() 更有优势
【讨论】:
我需要像在 ArrayList e.i.如果我放置第一个元素c
,然后放置元素a
,当我迭代一个集合时,我想以相同的顺序获取它们:c
、a
等。【参考方案6】:
您还可以从双向地图中获得一些实用程序,例如来自Google Guava 的BiMap
使用BiMap
,您可以非常有效地将整数(用于随机索引访问)映射到任何其他对象类型。 BiMap
s 是一对一的,因此任何给定的整数最多有一个与之关联的元素,并且任何元素都有一个关联的整数。它由两个 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中有序集的任何实现?的主要内容,如果未能解决你的问题,请参考以下文章