Java中的Ordered Set的任何实现?

时间:2012-01-03 12:42:00

标签: java collections set

如果有人熟悉Objective-C,则有一个名为NSOrderedSet的集合充当,其项目可以作为数组进行访问的。

Java中有这样的东西吗?

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

10 个答案:

答案 0 :(得分:103)

查看LinkedHashSet班级

答案 1 :(得分:29)

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

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

此外,您无法插入某个位置。

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

答案 2 :(得分:10)

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

还有SortedSetTreeSet实施的界面ConcurrentSkipListSet。两者都允许在元素的natural orderComparator中进行迭代,但不能随机访问或插入顺序。

对于既能通过索引进行高效访问又能有效实现set标准的数据结构,您需要skip list,但Java Standard API中没有该功能的实现,虽然我确信在互联网上找到一个很容易。

答案 3 :(得分:4)

答案 4 :(得分:4)

尝试使用实现java.util.TreeSetSortedSet

引用文档:

  

“元素是按照自然顺序排序的,或者是在创建时设置的比较器,具体取决于使用的构造函数”

请注意,添加,删除和包含时间成本日志(n)。

如果要将集合的内容作为数组访问,可以将其转换为:

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

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

答案 5 :(得分:1)

treeset是一个有序集,但您无法通过项索引进行访问,只需遍历或进入开始/结束。

答案 6 :(得分:0)

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

  

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

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

java.util.Arrays#copyOf

答案 7 :(得分:0)

来自IndexedTreeSet项目的

indexed-tree-map提供了此功能(按索引进行列表式访问的有序/有序集)。

答案 8 :(得分:0)

您也可以从双向地图中获取一些实用工具,例如来自BiMapGoogle Guava

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

答案 9 :(得分: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(); }
    }
}