J'ai besoin d'une collection qui garde l'ordre d'insertion et a des valeurs uniques. LinkedHashSet ressemble à la voie à suivre, mais il y a un problème - lorsque deux éléments sont égaux, il supprime le plus récent (ce qui est logique), voici un exemple:
set.add("one");
set.add("two");
set.add("three");
set.add("two");
LinkedHashSet
affichera:
one
,two
,three
Mais ce dont j'ai besoin c'est:
one
,three
,two
Quelle serait la meilleure solution ici? Existe-t-il une méthode de collecte/collections qui peut le faire ou dois-je l'implémenter manuellement?
La plupart des Java Collections peuvent être étendus pour peaufiner.
Sous-classe LinkedHashSet
, remplaçant la méthode add
.
class TweakedHashSet<T> extends LinkedHashSet<T> {
@Override
public boolean add(T e) {
// Get rid of old one.
boolean wasThere = remove(e);
// Add it.
super.add(e);
// Contract is "true if this set did not already contain the specified element"
return !wasThere;
}
}
Vous pouvez simplement utiliser une fonction spéciale de LinkedHashMap
:
Set<String> set = Collections.newSetFromMap(new LinkedHashMap<>(16, 0.75f, true));
set.add("one");
set.add("two");
set.add("three");
set.add("two");
System.out.println(set); // prints [one, three, two]
Dans le JRE d'Oracle, le LinkedHashSet
est de toute façon soutenu par un LinkedHashMap
, donc il n'y a pas beaucoup de différence fonctionnelle, mais le constructeur spécial utilisé ici configure le LinkedHashMap
pour changer l'ordre à chaque - accès non seulement sur insertion. Cela peut sembler trop, mais affecte en fait l'insertion de clés déjà contenues (valeurs au sens de Set
) uniquement. Les autres opérations Map
affectées (à savoir get
) ne sont pas utilisées par le Set
retourné.
Si vous n'utilisez pas Java 8, vous devez aider un peu le compilateur en raison de l'inférence de type limitée:
Set<String> set
= Collections.newSetFromMap(new LinkedHashMap<String, Boolean>(16, 0.75f, true));
mais la fonctionnalité est la même.
Lors de l'initialisation de votre LinkedHashSet, vous pouvez remplacer la méthode add.
Set<String> set = new LinkedHashSet<String>(){
@Override
public boolean add(String s) {
if(contains(s))
remove(s);
return super.add(s);
}
};
Maintenant, cela vous donne:
set.add("1");
set.add("2");
set.add("3");
set.add("1");
set.addAll(Collections.singleton("2"));
// [3, 1 ,2]
même la méthode addAll fonctionne.
Toutes les solutions fournies ci-dessus sont excellentes mais si nous ne voulons pas remplacer les collections déjà implémentées. Nous pouvons résoudre ce problème simplement en utilisant une ArrayList avec une petite astuce
Nous pouvons créer une méthode que vous utiliserez pour insérer des données dans votre liste
public static <T> void addToList(List<T> list, T element) {
list.remove(element); // Will remove element from list, if list contains it
list.add(element); // Will add element again to the list
}
Et nous pouvons appeler cette méthode pour ajouter un élément à notre liste
List<String> list = new ArrayList<>();
addToList(list, "one");
addToList(list, "two");
addToList(list, "three");
addToList(list, "two");
Le seul inconvénient ici est que nous devons appeler notre méthode addToList()
personnalisée à chaque fois au lieu de list.add()