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;
}
}
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);
// ...
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.
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.
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.
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");
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
.
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)