J'ai utilisé LinkedHashMap
car il est important de noter 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 entrée) ou la dernière.
Devrait-il y avoir une méthode comme first()
et last()
ou quelque chose comme ça?
Dois-je avoir un itérateur pour obtenir la première entrée de clé? C'est pourquoi j'ai utilisé LinkedHashMap
!
Merci!
La sémantique de LinkedHashMap
reste celle d'une carte, plutôt que celle d'un LinkedList
name__. Oui, il conserve l'ordre d'insertion, mais il s'agit d'un détail d'implémentation plutôt que d'un aspect de son interface.
Le moyen le plus rapide d'obtenir la "première" entrée est toujours entrySet().iterator().next()
. Obtenir la "dernière" entrée est possible, mais il faudra parcourir l'ensemble de l'entrée en appelant .next()
jusqu'à atteindre la dernière. while (iterator.hasNext()) { lastElement = iterator.next() }
edit : Toutefois, si vous souhaitez aller au-delà de l'API JavaSE, Apache Commons Collections a le sien LinkedMap
NAME _ implémentation, qui a des méthodes comme firstKey
NAME _ et lastKey
NAME _ , qui font ce que vous recherchez. 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
l'implémentation actuelle (Java 8) garde une trace de sa queue. Si les performances sont préoccupantes et/ou si la carte est de grande taille, vous pouvez accéder à ce champ par réflexion.
Étant donné que la mise en œuvre peut changer, il est probablement judicieux de recourir également à une stratégie de repli. Vous souhaiterez peut-être enregistrer quelque chose si une exception est levée afin de savoir que l'implémentation a changé.
Cela 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);
}
Je sais que je suis arrivé trop tard mais je voudrais proposer quelques alternatives, pas quelque chose d'extraordinaire mais quelques cas qui ne sont pas mentionnés ici. Au cas où quelqu'un se soucierait moins de l'efficacité, mais qu'il souhaite quelque chose de plus simple (peut-être trouver la dernière valeur d'entrée avec une seule ligne de code), tout cela sera simplifié avec l'arrivée de Java 8 . Je propose des scénarios utiles.
Par souci d'exhaustivité, je compare ces alternatives à la solution de tableaux déjà mentionnée dans ce message par d'autres utilisateurs. Je résume tous les cas et je pense qu'ils seraient utiles (que la performance compte ou non), en particulier pour les nouveaux développeurs, dépend toujours de la question de chaque problème
Je me suis inspiré de la réponse précédente pour faire les comparaisons suivantes. Cette solution appartient à @feresr.
public static String FindLasstEntryWithArrayMethod() {
return String.valueOf(linkedmap.entrySet().toArray()[linkedmap.size() - 1]);
}
Semblable à la première solution avec des performances légèrement 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();
}
Cette méthode réduira l'ensemble d'éléments jusqu'à obtenir le dernier élément de flux. De plus, il ne retournera que des résultats déterministes
public static String FindLasstEntryWithReduceMethod() {
return linkedmap.entrySet().stream().reduce((first, second) -> second).orElse(null).getValue();
}
Cette méthode obtiendra le dernier élément du flux simplement en sautant tous les éléments précédents.
public static String FindLasstEntryWithSkipFunctionMethod() {
final long count = linkedmap.entrySet().stream().count();
return linkedmap.entrySet().stream().skip(count - 1).findFirst().get().getValue();
}
Iterables.getLast de Google Guava. Il a une optimisation pour Listes et SortedSets aussi
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 les performances 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
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 Set.
Mais je pense que parcourir les entrées de l'ensemble d'entrées et obtenir les première et dernière entrées est une meilleure approche.
L'utilisation de méthodes de tableau conduit à l'avertissement du formulaire "... nécessite une conversion non vérifiée pour se conformer à ..." qui ne peut pas être corrigé [mais ne peut être supprimé qu'en utilisant l'annotation @SuppressWarnings ("décochée")].
Voici un petit exemple pour illustrer 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
</ pre></ code>
C'est un peu sale, mais vous pouvez remplacer la méthode removeEldestEntry
de LinkedHashMap, ce qui vous conviendra peut-être comme membre anonyme anonyme:
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 sur votre membre eldest
. Il sera mis à jour chaque fois que vous effectuez une put
.
Il devrait également être facile de remplacer put
et de définir youngest
...
@Override
public Splat put(Integer key, Splat value) {
youngest = value;
return super.put(key, value);
}
Tout se passe lorsque vous commencez à supprimer des entrées; Je n'ai pas trouvé le moyen de minimiser cela.
C'est très énervant que vous ne puissiez pas autrement accéder à la tête ou à la queue de façon sensée ...
Peut-être quelque chose comme ça:
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;
}
Je recommanderais d'utiliser ConcurrentSkipListMap qui a les méthodes firstKey()
et lastKey()
Suggestion:
map.remove(map.keySet().iterator().next());
à droite, vous devez énumérer manuellement le jeu de clés jusqu'à la fin de la liste liée, puis récupérer l'entrée par clé et la renvoyer.
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 cela.
private String getDefaultPlayerType()
{
String defaultPlayerType = "";
for(LinkedHashMap.Entry<String,Integer> entry : getLeagueByName(currentLeague).getStatisticsOrder().entrySet())
{
defaultPlayerType = entry.getKey();
break;
}
return defaultPlayerType;
}
Si vous avez également besoin du dernier élément - je chercherais à inverser l'ordre de votre carte - enregistrez-le dans une variable temporaire, accédez au premier élément de la carte inversée (donc ce serait votre dernier élément) 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, accordez-leur un vote positif:) J'espère que cela pourra aider quelqu'un.
Bien que linkedHashMap ne fournisse aucune méthode pour obtenir le premier, le dernier ou un objet spécifique.
Mais c'est assez trivial pour obtenir:
utilise maintenant iterator sur al object; vous pouvez obtenir n'importe quel objet.