Как получить индекс данного элемента LinkedHashSet без итерации? - PullRequest
22 голосов
/ 15 августа 2011

Это вообще возможно?

Скажем, у вас есть

private Set<String> names = new LinkedHashSet<String>();

и Strings "Майк", "Джон", "Карен".

Естьможно получить "1" взамен "что такое индекс" Джона "без итерации?

Следующее работает нормально ... с этим вопросом мне интересно, есть ли лучший способ

for (String s : names) {
    ++i;
    if (s.equals(someRandomInputString)) {
        break;
    }
}

Ответы [ 7 ]

23 голосов
/ 15 августа 2011

Интерфейс Set не имеет ничего общего с методом indexOf().Вам действительно нужно перебрать его или использовать интерфейс List , который предлагает метод indexOf() .

преобразование Set в List довольно тривиально, нужно просто передать Set через конструктор реализации List.Например,

List<String> nameList = new ArrayList<String>(nameSet);
// ...
4 голосов
/ 31 июля 2015

Вот реализация, которая делает вставки, удаления, сохранения, поддерживаемые массивом для достижения o (1) при получении (индекс).

/**
 * @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<E> extends LinkedHashSet<E> {
        private final ArrayList<E> list = new ArrayList<>();

        public $IndexLinkedHashSet(int initialCapacity, float loadFactor) {
                super(initialCapacity, loadFactor);
        }
        public $IndexLinkedHashSet() {
                super();
        }
        public $IndexLinkedHashSet(int initialCapacity) {
                super(initialCapacity);
        }
        public $IndexLinkedHashSet(Collection<? extends E> 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<? extends E> c) {
                boolean modified = false;
                for (E e : c)
                        if (add(e))
                                modified = true;
                return modified;
        }

}

Чтобы проверить это:

public static void main(String[] args) {

        $IndexLinkedHashSet<String> abc = new $IndexLinkedHashSet<String>();
        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++;
        }

}

Какие выходы:

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

Конечно, remove, removeAll, retainAll теперь имеет ту же или худшую производительность, что и ArrayList. Но я ими не пользуюсь и с этим у меня все в порядке.

Наслаждайтесь!

EDIT:

Вот другая реализация , которая не расширяет LinkedHashSet, потому что это избыточно. Вместо этого он использует HashSet и 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<E> implements Set<E> {
        private final ArrayList<E> list = new ArrayList<>( );
        private final HashSet<E>   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<? extends E> 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<E> iterator() {
                return list.iterator();
        }

        @Override
        public synchronized Object[] toArray() {
                return list.toArray();
        }

        @Override
        public synchronized <T> T[] toArray(T[] a) {
                return list.toArray(a);
        }
}

Теперь у вас есть две реализации, я бы предпочел вторую.

3 голосов
/ 15 августа 2011

Как правило, Set не может возвращать индекс, поскольку он не обязательно четко определен для конкретной реализации Set. Например, в документации HashSet сказано:

Не дает никаких гарантий относительно порядка итерации множества; в частности, это не гарантирует, что заказ останется постоянным во времени.

Таким образом, вы не должны говорить, что тип - это Set, если вы ожидаете, что это Set, реализующий порядок сомов.

3 голосов
/ 15 августа 2011

Я не верю, но вы можете создать класс-оболочку LinkedHashSetWithIndex, который будет выполнять итерацию за вас, или сохранить отдельную таблицу с индексами каждой записи, если снижение производительности приемлемо для вашего варианта использования.

2 голосов
/ 15 августа 2011

A Set - это «коллекция, которая не содержит повторяющихся элементов», и она не поддерживает порядок своих элементов.http://download.oracle.com/javase/6/docs/api/java/util/Set.html

Код, который вы дали, не обязательно будет возвращать 1 каждый раз.Итерация по Set не гарантирует, что итерация будет выполняться в одном и том же порядке каждый раз;гарантированно итерация каждого элемента выполняется только один раз.

Поскольку вы, похоже, заботитесь о порядке элементов, вам нужно использовать List вместо Set.

2 голосов
/ 15 августа 2011

Хотя это не так эффективно для машины, это достигается в одну строку:

int index = new ArrayList<String>(names).indexOf("John");
0 голосов
/ 15 августа 2011

Лучшего способа не существует, только один выровненный (который также использует итератор, но неявно):

new ArrayList(names).get(0)
...