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!

98
demandé sur rogerdpack 2009-12-20 20:54:02

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.

121
répondu skaffman 2015-12-09 17:04:39

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) :)

13
répondu FRR 2017-07-12 15:12:58

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);
}
8
répondu assylias 2015-11-27 15:21:46

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

6
répondu sateesh 2009-12-21 05:12:19

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
4
répondu xXxpRoGrAmmErxXx 2018-04-16 14:38:08

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;
}
2
répondu user2127649 2013-03-22 00:03:42

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 ...

2
répondu robert 2016-02-10 11:19:57

je recommande d'utiliser ConcurrentSkipListMap qui a firstKey() et lastKey() méthodes

1
répondu Doua Beri 2012-09-19 22:43:55

Suggestion:

map.remove(map.keySet().iterator().next());
1
répondu Tadeu Jr. 2016-03-31 15:27:41

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.

0
répondu rai.skumar 2012-11-07 07:14:42

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.

0
répondu ryvianstyron 2017-05-23 12:10:34

à 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.

0
répondu unknownwill 2018-08-18 05:30:14