Any implementation of Ordered Set in Java?

后端 未结 10 1908
说谎
说谎 2020-12-01 09:00

If anybody is familiar with Objective-C there is a collection called NSOrderedSet that acts as Set and its items can be accessed as an Array

相关标签:
10条回答
  • 2020-12-01 09:22

    treeset is an ordered set, but you can't access via an items index, just iterate through or go to beginning/end.

    0 讨论(0)
  • 2020-12-01 09:28

    IndexedTreeSet from the indexed-tree-map project provides this functionality (ordered/sorted set with list-like access by index).

    0 讨论(0)
  • 2020-12-01 09:30

    I had a similar problem. I didn't quite need an ordered set but more a list with a fast indexOf/contains. As I didn't find anything out there I implemented one myself. Here's the code, it implements both Set and List, though not all bulk list operations are as fast as the ArrayList versions.

    disclaimer: not tested

    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 讨论(0)
  • 2020-12-01 09:36

    Every Set has an iterator(). A normal HashSet's iterator is quite random, a TreeSet does it by sort order, a LinkedHashSet iterator iterates by insert order.

    You can't replace an element in a LinkedHashSet, however. You can remove one and add another, but the new element will not be in the place of the original. In a LinkedHashMap, you can replace a value for an existing key, and then the values will still be in the original order.

    Also, you can't insert at a certain position.

    Maybe you'd better use an ArrayList with an explicit check to avoid inserting duplicates.

    0 讨论(0)
提交回复
热议问题