How to obtain index of a given LinkedHashSet element without iteration?

后端 未结 8 1654
無奈伤痛
無奈伤痛 2020-12-14 16:45

Is it even possible?

Say you have

private Set names = new LinkedHashSet();

and Strings ar

8条回答
  •  猫巷女王i
    2020-12-14 16:57

    Here is an implementation that does insertions, removals, retainings, backed by an arraylist to achieve o(1) on get(index).

    /**
     * @Author Mo. Joseph
     *
     * Allows you to call get with o(1) instead of o(n) to get an instance by index
     */
    public static final class $IndexLinkedHashSet extends LinkedHashSet {
            private final ArrayList list = new ArrayList<>();
    
            public $IndexLinkedHashSet(int initialCapacity, float loadFactor) {
                    super(initialCapacity, loadFactor);
            }
            public $IndexLinkedHashSet() {
                    super();
            }
            public $IndexLinkedHashSet(int initialCapacity) {
                    super(initialCapacity);
            }
            public $IndexLinkedHashSet(Collection c) {
                    super(c);
            }
    
            @Override
            public synchronized boolean add(E e) {
                    if ( super.add(e) ) {
                            return list.add(e);
                    }
                    return false;
            }
    
            @Override
            public synchronized boolean remove(Object o) {
                    if ( super.remove(o) ) {
                            return list.remove(o);
                    }
                    return false;
            }
    
            @Override
            public synchronized void clear() {
                    super.clear();
                    list.clear();
            }
    
            public synchronized E get(int index) {
                    return list.get(index);
            }
    
            @Override
            public synchronized boolean removeAll(Collection c) {
                    if ( super.removeAll(c) ) {
                            return list.removeAll(c);
                    }
                    return true;
            }
    
            @Override
            public synchronized boolean retainAll(Collection c) {
                    if ( super.retainAll(c) ) {
                            return list.retainAll(c);
                    }
                    return false;
            }
    
            /**
             * Copied from super class
             */
            @Override
            public synchronized boolean addAll(Collection c) {
                    boolean modified = false;
                    for (E e : c)
                            if (add(e))
                                    modified = true;
                    return modified;
            }
    
    }
    

    To test it:

    public static void main(String[] args) {
    
            $IndexLinkedHashSet abc = new $IndexLinkedHashSet();
            abc.add("8");
            abc.add("8");
            abc.add("8");
            abc.add("2");
            abc.add("3");
            abc.add("4");
            abc.add("1");
            abc.add("5");
            abc.add("8");
    
            System.out.println("Size: " + abc.size());
            int i = 0;
            while ( i < abc.size()) {
                    System.out.println( abc.get(i) );
                    i++;
            }
    
            abc.remove("8");
            abc.remove("5");
    
            System.out.println("Size: " + abc.size());
            i = 0;
            while ( i < abc.size()) {
                    System.out.println( abc.get(i) );
                    i++;
            }
    
            abc.clear();
    
            System.out.println("Size: " + abc.size());
            i = 0;
            while ( i < abc.size()) {
                    System.out.println( abc.get(i) );
                    i++;
            }
    
    }
    

    Which outputs:

    Size: 6
    8
    2
    3
    4
    1
    5
    Size: 4
    2
    3
    4
    1
    Size: 0
    

    Ofcourse remove, removeAll, retainAll now has the same or worse performance as ArrayList. But I do not use them and so I am ok with that.

    Enjoy!

    EDIT:

    Here is another implementation, which does not extend LinkedHashSet because that's redundant. Instead it uses a HashSet and an ArrayList.

    /**
     * @Author Mo. Joseph
     *
     * Allows you to call get with o(1) instead of o(n) to get an instance by index
     */
    public static final class $IndexLinkedHashSet implements Set {
            private final ArrayList list = new ArrayList<>( );
            private final HashSet   set  = new HashSet<>  ( );
    
            public synchronized boolean add(E e) {
                    if ( set.add(e) ) {
                            return list.add(e);
                    }
                    return false;
            }
    
            public synchronized boolean remove(Object o) {
                    if ( set.remove(o) ) {
                            return list.remove(o);
                    }
                    return false;
            }
    
            @Override
            public boolean containsAll(Collection c) {
                    return set.containsAll(c);
            }
    
            public synchronized void clear() {
                    set.clear();
                    list.clear();
            }
    
            public synchronized E get(int index) {
                    return list.get(index);
            }
    
            public synchronized boolean removeAll(Collection c) {
                    if ( set.removeAll(c) ) {
                            return list.removeAll(c);
                    }
                    return true;
            }
    
            public synchronized boolean retainAll(Collection c) {
                    if ( set.retainAll(c) ) {
                            return list.retainAll(c);
                    }
                    return false;
            }
    
            public synchronized boolean addAll(Collection c) {
                    boolean modified = false;
                    for (E e : c)
                            if (add(e))
                                    modified = true;
                    return modified;
            }
    
            @Override
            public synchronized int size() {
                    return set.size();
            }
    
            @Override
            public synchronized boolean isEmpty() {
                    return set.isEmpty();
            }
    
            @Override
            public synchronized boolean contains(Object o) {
                    return set.contains(o);
            }
    
            @Override
            public synchronized Iterator iterator() {
                    return list.iterator();
            }
    
            @Override
            public synchronized Object[] toArray() {
                    return list.toArray();
            }
    
            @Override
            public synchronized  T[] toArray(T[] a) {
                    return list.toArray(a);
            }
    }
    

    Now you have two implementations, I would prefer the second one.

提交回复
热议问题