Java LinkedHashMap obtenir la première ou la dernière entrée
j'ai utilisé LinkedHashMap
parce que c'est important l'ordre dans lequel les clés sont entrées dans la carte.
Mais maintenant, je veux obtenir la valeur de la clé en premier lieu (la première entrée de l'entrée) ou la dernière.
Devrait-il y avoir une méthode comme first()
et last()
ou quelque chose comme ça?
ai-je besoin d'un itérateur pour obtenir la première entrée de clé? C'est pourquoi j'ai utilisé LinkedHashMap
!
Merci!
12 réponses
la sémantique de LinkedHashMap
est encore celle d'une carte, plutôt que celle d'une LinkedList
. Il conserve l'ordre d'insertion, oui, mais c'est un détail d'implémentation, plutôt que d'un aspect de son interface.
le moyen Le plus rapide pour obtenir la "première entrée" est toujours entrySet().iterator().next()
. Obtenir la" dernière "entrée est possible, mais il faudra itérer sur l'ensemble de l'entrée en appelant .next()
jusqu'à ce que vous atteigniez la dernière. while (iterator.hasNext()) { lastElement = iterator.next() }
éditer : cependant, si vous êtes prêt à aller au-delà de L'API JavaSE, Apache collections communes a ses propres LinkedMap
mise en œuvre, qui a des méthodes comme firstKey
et lastKey
, qui font ce que vous cherchez. L'interface est considérablement plus riche.
Pouvez-vous essayer de faire quelque chose comme (pour obtenir la dernière entrée):
linkedHashMap.entrySet().toArray()[linkedHashMap.size() -1];
c'est O(N) :)
LinkedHashMap
current implementation (Java 8) conserve la trace de sa queue. Si la performance est un problème et / ou si la carte est de grande taille, vous pouvez accéder à ce champ par réflexion.
Parce que la mise en œuvre peut changer c'est probablement une bonne idée d'avoir une stratégie de repli. Vous pouvez vouloir enregistrer quelque chose si une exception est lancée pour que vous sachiez que l'implémentation a changé.
ça pourrait ressembler à:
public static <K, V> Entry<K, V> getFirst(Map<K, V> map) {
if (map.isEmpty()) return null;
return map.entrySet().iterator().next();
}
public static <K, V> Entry<K, V> getLast(Map<K, V> map) {
try {
if (map instanceof LinkedHashMap) return getLastViaReflection(map);
} catch (Exception ignore) { }
return getLastByIterating(map);
}
private static <K, V> Entry<K, V> getLastByIterating(Map<K, V> map) {
Entry<K, V> last = null;
for (Entry<K, V> e : map.entrySet()) last = e;
return last;
}
private static <K, V> Entry<K, V> getLastViaReflection(Map<K, V> map) throws NoSuchFieldException, IllegalAccessException {
Field tail = map.getClass().getDeclaredField("tail");
tail.setAccessible(true);
return (Entry<K, V>) tail.get(map);
}
une autre façon d'obtenir la première et la dernière entrée D'un LinkedHashMap est d'utiliser la méthode" toArray " de L'interface de jeu.
mais je pense itérer sur les entrées dans le jeu d'entrée et obtenir la première et la dernière entrée est une meilleure approche.
L'utilisation des méthodes array conduit à l'avertissement de la forme "...a besoin d'une conversion non contrôlée pour se conformer ..." qui ne peut être fixé [mais ne peut être supprimé qu'en annotation @SuppressWarnings("unchecked")].
voici un petit exemple pour démontrer l'utilisation de la méthode "toArray":
public static void main(final String[] args) {
final Map<Integer,String> orderMap = new LinkedHashMap<Integer,String>();
orderMap.put(6, "Six");
orderMap.put(7, "Seven");
orderMap.put(3, "Three");
orderMap.put(100, "Hundered");
orderMap.put(10, "Ten");
final Set<Entry<Integer, String>> mapValues = orderMap.entrySet();
final int maplength = mapValues.size();
final Entry<Integer,String>[] test = new Entry[maplength];
mapValues.toArray(test);
System.out.print("First Key:"+test[0].getKey());
System.out.println(" First Value:"+test[0].getValue());
System.out.print("Last Key:"+test[maplength-1].getKey());
System.out.println(" Last Value:"+test[maplength-1].getValue());
}
// the output geneated is :
First Key:6 First Value:Six
Last Key:10 Last Value:Ten
je sais que je suis arrivé trop tard mais je voudrais proposer quelques alternatives, pas quelque chose d'extraordinaire mais quelques cas que personne n'a mentionnés ici. Dans le cas où quelqu'un ne se soucie pas tellement de l'efficacité mais il veut quelque chose avec plus de simplicité(peut-être trouver la dernière valeur d'entrée avec une ligne de code), tout cela sera très simplifié avec l'arrivée de Java 8 . Je propose quelques scénarios utiles.
pour l'amour du completeness, je compare ces alternatives avec la solution des tableaux qui ont déjà été mentionnés dans ce post par d'autres utilisateurs. Je résume tous les cas et je pense qu'ils seraient utiles(quand la performance importe ou pas) surtout pour les nouveaux développeurs, dépend toujours de la question de chaque problème
Alternatives Possibles
Utilisation de la Méthode de la baie
Je l'ai pris de la réponse précédente pour faire les comparaisons suivantes. Ce la solution appartient à @feresr.
public static String FindLasstEntryWithArrayMethod() {
return String.valueOf(linkedmap.entrySet().toArray()[linkedmap.size() - 1]);
}
Utilisation de la liste de tableaux Méthode
similaire à la première solution avec un peu de performances différentes
public static String FindLasstEntryWithArrayListMethod() {
List<Entry<Integer, String>> entryList = new ArrayList<Map.Entry<Integer, String>>(linkedmap.entrySet());
return entryList.get(entryList.size() - 1).getValue();
}
Méthode De Réduction
cette méthode réduira l'ensemble des éléments jusqu'à obtenir le dernier élément de stream. En outre, il ne retournera que des résultats déterministes
public static String FindLasstEntryWithReduceMethod() {
return linkedmap.entrySet().stream().reduce((first, second) -> second).orElse(null).getValue();
}
SkipFunction Method
cette méthode va obtenir le dernier élément du ruisseau en sautant simplement tous les éléments avant lui
public static String FindLasstEntryWithSkipFunctionMethod() {
final long count = linkedmap.entrySet().stream().count();
return linkedmap.entrySet().stream().skip(count - 1).findFirst().get().getValue();
}
Iterable Alternative
Iterables.getLast de Google Guava. Il a une certaine optimisation pour Les listes et les SortedSets trop
public static String FindLasstEntryWithGuavaIterable() {
return Iterables.getLast(linkedmap.entrySet()).getValue();
}
voici le code source complet
import com.google.common.collect.Iterables;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class PerformanceTest {
private static long startTime;
private static long endTime;
private static LinkedHashMap<Integer, String> linkedmap;
public static void main(String[] args) {
linkedmap = new LinkedHashMap<Integer, String>();
linkedmap.put(12, "Chaitanya");
linkedmap.put(2, "Rahul");
linkedmap.put(7, "Singh");
linkedmap.put(49, "Ajeet");
linkedmap.put(76, "Anuj");
//call a useless action so that the caching occurs before the jobs starts.
linkedmap.entrySet().forEach(x -> {});
startTime = System.nanoTime();
FindLasstEntryWithArrayListMethod();
endTime = System.nanoTime();
System.out.println("FindLasstEntryWithArrayListMethod : " + "took " + new BigDecimal((endTime - startTime) / 1000000.000).setScale(3, RoundingMode.CEILING) + " milliseconds");
startTime = System.nanoTime();
FindLasstEntryWithArrayMethod();
endTime = System.nanoTime();
System.out.println("FindLasstEntryWithArrayMethod : " + "took " + new BigDecimal((endTime - startTime) / 1000000.000).setScale(3, RoundingMode.CEILING) + " milliseconds");
startTime = System.nanoTime();
FindLasstEntryWithReduceMethod();
endTime = System.nanoTime();
System.out.println("FindLasstEntryWithReduceMethod : " + "took " + new BigDecimal((endTime - startTime) / 1000000.000).setScale(3, RoundingMode.CEILING) + " milliseconds");
startTime = System.nanoTime();
FindLasstEntryWithSkipFunctionMethod();
endTime = System.nanoTime();
System.out.println("FindLasstEntryWithSkipFunctionMethod : " + "took " + new BigDecimal((endTime - startTime) / 1000000.000).setScale(3, RoundingMode.CEILING) + " milliseconds");
startTime = System.currentTimeMillis();
FindLasstEntryWithGuavaIterable();
endTime = System.currentTimeMillis();
System.out.println("FindLasstEntryWithGuavaIterable : " + "took " + (endTime - startTime) + " milliseconds");
}
public static String FindLasstEntryWithReduceMethod() {
return linkedmap.entrySet().stream().reduce((first, second) -> second).orElse(null).getValue();
}
public static String FindLasstEntryWithSkipFunctionMethod() {
final long count = linkedmap.entrySet().stream().count();
return linkedmap.entrySet().stream().skip(count - 1).findFirst().get().getValue();
}
public static String FindLasstEntryWithGuavaIterable() {
return Iterables.getLast(linkedmap.entrySet()).getValue();
}
public static String FindLasstEntryWithArrayListMethod() {
List<Entry<Integer, String>> entryList = new ArrayList<Map.Entry<Integer, String>>(linkedmap.entrySet());
return entryList.get(entryList.size() - 1).getValue();
}
public static String FindLasstEntryWithArrayMethod() {
return String.valueOf(linkedmap.entrySet().toArray()[linkedmap.size() - 1]);
}
}
Voici la sortie avec la performance de chaque méthode
FindLasstEntryWithArrayListMethod : took 0.162 milliseconds
FindLasstEntryWithArrayMethod : took 0.025 milliseconds
FindLasstEntryWithReduceMethod : took 2.776 milliseconds
FindLasstEntryWithSkipFunctionMethod : took 3.396 milliseconds
FindLasstEntryWithGuavaIterable : took 11 milliseconds
peut-être quelque chose comme ceci:
LinkedHashMap<Integer, String> myMap;
public String getFirstKey() {
String out = null;
for (int key : myMap.keySet()) {
out = myMap.get(key);
break;
}
return out;
}
public String getLastKey() {
String out = null;
for (int key : myMap.keySet()) {
out = myMap.get(key);
}
return out;
}
c'est un peu sale, mais vous pouvez outrepasser la méthode removeEldestEntry
de LinkedHashMap, qu'il pourrait vous convenir de faire en tant que Membre anonyme privé:
private Splat eldest = null;
private LinkedHashMap<Integer, Splat> pastFutures = new LinkedHashMap<Integer, Splat>() {
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Splat> eldest) {
eldest = eldest.getValue();
return false;
}
};
ainsi vous pourrez toujours obtenir la première entrée à votre membre eldest
. Il sera mis à jour chaque fois que vous effectuez un put
.
il devrait aussi être facile de remplacer put
et de régler youngest
...
@Override
public Splat put(Integer key, Splat value) {
youngest = value;
return super.put(key, value);
}
C'toutes les pauses vers le bas quand vous commencez à supprimer des entrées cependant; n'ont pas trouvé un moyen de kludge cela.
il est très ennuyeux que vous ne pouvez pas autrement accéder à la tête ou à la queue d'une manière raisonnable ...
je recommande d'utiliser ConcurrentSkipListMap qui a firstKey()
et lastKey()
méthodes
même si linkedHashMap ne fournit aucune méthode pour obtenir le premier, Le Dernier ou tout autre objet spécifique.
Mais c'est assez trivial pour obtenir :
- map orderMap = new LinkedHashMap ();
Paramètre al = orderMap.keySet ();
utilise maintenant iterator sur un objet al ; vous pouvez obtenir n'importe quel objet.
Oui j'ai rencontré le même problème, mais heureusement je n'ai besoin que du premier élément... - C'est ce que j'ai fait pour elle.
private String getDefaultPlayerType()
{
String defaultPlayerType = "";
for(LinkedHashMap.Entry<String,Integer> entry : getLeagueByName(currentLeague).getStatisticsOrder().entrySet())
{
defaultPlayerType = entry.getKey();
break;
}
return defaultPlayerType;
}
si vous avez besoin du dernier élément aussi bien - je chercherais comment inverser l'ordre de votre carte - le stocker dans une variable temp, accéder au premier élément dans la carte inversée(donc ce serait votre dernier élément), tuer la variable temp.
voici quelques bonnes réponses sur la façon d'Inverser l'ordre d'un hashmap:
comment itérer hashmap dans L'ordre inverse en Java
Si vous utilisez l'aide du lien ci-dessus, veuillez donner les votes :) J'espère que cela peut aider quelqu'un.
à droite, vous devez énumérer manuellement les clés jusqu'à la fin de la liste de liens, puis récupérer l'entrée par clé et retourner cette entrée.