web-dev-qa-db-fra.com

Comment obtenir l'index d'un élément LinkedHashSet donné sans itération?

Est-ce même possible?

Dis que tu as 

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

et Strings sont "Mike", "John", "Karen".

Est-il possible d'obtenir "1" en retour de "quel est l'index de" John "sans itération?

Ce qui suit fonctionne bien .. avec cette question, je me demande s’il existe un meilleur moyen

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

L'interface Set n'a pas une méthode semblable à celle d'une méthode indexOf(). Vous auriez vraiment besoin de le parcourir ou d’utiliser l’interface List qui propose une méthode indexOf() .

Si vous le souhaitez, convertir Set en List est assez simple, il vous suffit de passer Set par le constructeur de l’implémentation List. Par exemple.

List<String> nameList = new ArrayList<String>(nameSet);
// ...
22
BalusC

Voici une implémentation qui fait des insertions, des suppressions, des rétentions, appuyées par un arraylist pour obtenir o(1) sur 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<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;
        }

}

Pour le tester: 

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++;
        }

}

Quelles sorties: 

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

Bien sûr, supprimer, supprimerTout, conserverTout a maintenant la même performance ou une performance inférieure à celle de ArrayList. Mais je ne les utilise pas et je suis d'accord avec ça.

Prendre plaisir!

MODIFIER:

Voici une autre implémentation , qui n'étend pas LinkedHashSet car elle est redondante. Au lieu de cela, il utilise un HashSet et une 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);
        }
}

Maintenant que vous avez deux implémentations, je préférerais la seconde. 

4
momomo

Il n'est généralement pas possible pour un ensemble de renvoyer l'index, car il n'est pas nécessairement bien défini pour l'implémentation de cet ensemble. Par exemple, il est indiqué dans la documentation de HashSet

Il ne fait aucune garantie quant à l'ordre d'itération de l'ensemble; en particulier, cela ne garantit pas que l'ordre restera constant dans le temps.

Donc, vous ne devriez pas dire que le type est Set alors que vous vous attendez à un Set qui implémente un ordre.

3
ReyCharles

Je ne le crois pas, mais vous pouvez créer une classe wrapper LinkedHashSetWithIndex qui effectuerait l'itération à votre place ou conserver une table séparée avec les index de chaque entrée si la diminution des performances est acceptable pour votre cas d'utilisation.

3
JRL

Bien que cela ne soit pas aussi efficace pour la machine, cela le fait en une ligne:

int index = new ArrayList<String>(names).indexOf("John");
3
Bohemian

Set est "une collection qui ne contient aucun élément dupliqué" et ne maintient pas l'ordre de ses éléments. http://download.Oracle.com/javase/6/docs/api/Java/util/Set.html

Le code que vous avez donné ne renverra pas nécessairement 1 à chaque fois. Itérer dans une Set n'est pas garanti pour itérer dans le même ordre à chaque fois; il est seulement garanti de parcourir chaque élément une fois.

Puisque vous semblez vous soucier de l'ordre des éléments, vous devez utiliser une variable List au lieu d'une Set.

2
avh

Une meilleure façon de le faire n’est pas, mais une seule ligne (qui utilise aussi l’itérateur, mais implicitement):

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