Java LinkedHashMap获取第一个或最后一个条目

时间:2009-12-20 17:54:03

标签: java dictionary linkedhashmap

我使用过LinkedHashMap,因为按键在地图中输入的顺序非常重要。

但是现在我想首先获得key的值(第一个输入的条目)或者最后一个。

是否应该有类似first()last()之类的方法?

我是否需要一个迭代器来获取第一个键入口?这就是我使用LinkedHashMap的原因!

谢谢!

15 个答案:

答案 0 :(得分:133)

LinkedHashMap的语义仍然是Map的语义,而不是LinkedList的语义。它保留了插入顺序,是的,但这是一个实现细节,而不是其界面的一个方面。

获得“第一”条目的最快方法仍然是entrySet().iterator().next()。获取“最后”条目是可能的,但是需要通过调用.next()来迭代整个条目集,直到到达最后一个。 while (iterator.hasNext()) { lastElement = iterator.next() }

编辑:但是,如果您愿意超越JavaSE API,Apache Commons Collections有自己的LinkedMap实现,其中包含firstKey等方法和lastKey,它们可以满足您的需求。界面相当丰富。

答案 1 :(得分:18)

你能尝试做类似的事情(获得最后一个条目):

linkedHashMap.entrySet().toArray()[linkedHashMap.size() -1];

是O(N):)

答案 2 :(得分:11)

LinkedHashMap当前实现(Java 8)跟踪其尾部。如果考虑到性能和/或地图的大小,您可以通过反射访问该字段。

由于实施可能会发生变化,因此也可能采用后备策略。如果抛出异常,您可能希望记录某些内容,因此您知道实现已更改。

它可能看起来像:

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);
}

答案 3 :(得分:10)

我知道我来得太晚了,但我想提供一些替代方案,而不是一些特别的东西,但有些案例没有提到这里。如果有人不太关心效率,但他想要一些更简单的东西(也许用一行代码找到最后一个入口值),所有这些都会随着到达而变得非常简单   Java 8 。我提供了一些有用的场景。

为了完整起见,我将这些备选方案与其他用户在本文中提到的数组解决方案进行了比较。我总结了所有的情况,我认为它们是有用的(当性能确实重要或没有时),特别是对于新的开发人员,总是取决于每个问题的问题

可能的替代方案

数组方法的用法

我从之前的回答中得到了它,以进行以下比较。该解决方案属于@feresr。

  public static String FindLasstEntryWithArrayMethod() {
        return String.valueOf(linkedmap.entrySet().toArray()[linkedmap.size() - 1]);
    }

ArrayList方法的用法

与第一种具有不同性能的解决方案类似

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 FindLasstEntryWithReduceMethod() {
        return linkedmap.entrySet().stream().reduce((first, second) -> second).orElse(null).getValue();
    }

SkipFunction Method

此方法将通过简单地跳过它之前的所有元素来获取流的最后一个元素

public static String FindLasstEntryWithSkipFunctionMethod() {
        final long count = linkedmap.entrySet().stream().count();
        return linkedmap.entrySet().stream().skip(count - 1).findFirst().get().getValue();
    }

Iterable Alternative

  

来自Google Guava的Iterables.getLast。它有一些优化   Lists和SortedSets也是

public static String FindLasstEntryWithGuavaIterable() {
        return Iterables.getLast(linkedmap.entrySet()).getValue();
    }

以下是完整的源代码

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]);
    }
}

以下是每种方法的性能输出

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 :(得分:6)

获取LinkedHashMap的第一个和最后一个条目的另一种方法是使用Set接口的“toArray”方法。

但我认为迭代条目集中的条目并获得第一个和最后一个条目是一种更好的方法。

使用数组方法会导致形式警告“...需要未经检查的转换以符合......”这是无法修复的[但只能通过使用注释来抑制@SuppressWarnings( “未登记”)]。

这是一个小例子来演示“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

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

答案 5 :(得分:3)

它有点脏,但您可以覆盖LinkedHashMap的removeEldestEntry方法,它可能适合您作为私人匿名成员:

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;
    }
};

因此,您始终可以获得eldest成员的第一个条目。每次执行put时都会更新。

覆盖put并设置youngest ...

也应该很容易
    @Override
    public Splat put(Integer key, Splat value) {

        youngest = value;
        return super.put(key, value);
    }

当你开始删除条目时,这一切都会崩溃;没有找到解决这个问题的方法。

非常烦人的是,你无法以明智的方式接触头部或尾部...

答案 6 :(得分:2)

也许是这样的:

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;
}

答案 7 :(得分:1)

我建议使用ConcurrentSkipListMap firstKey()lastKey()方法

答案 8 :(得分:1)

建议:

map.remove(map.keySet().iterator().next());

答案 9 :(得分:1)

对于第一个元素,请使用entrySet().iterator().next()并在1次迭代后停止迭代。 对于最后一个,最简单的方法是每当执行map.put时将键保存在变量中。

答案 10 :(得分:1)

        import java.util.Arrays;
        import java.util.LinkedHashMap;
        import java.util.List;
        import java.util.Map;

        public class Scratch {
           public static void main(String[] args) {

              // Plain java version

              Map<String, List<Integer>> linked = new LinkedHashMap<>();
              linked.put("a", Arrays.asList(1, 2, 3));
              linked.put("aa", Arrays.asList(1, 2, 3, 4));
              linked.put("b", Arrays.asList(1, 2, 3, 4, 5));
              linked.put("bb", Arrays.asList(1, 2, 3, 4, 5, 6));

              System.out.println("linked = " + linked);

              String firstKey = getFirstKey(linked);
              System.out.println("firstKey = " + firstKey);
              List<Integer> firstEntry = linked.get(firstKey);
              System.out.println("firstEntry = " + firstEntry);

              String lastKey = getLastKey(linked);
              System.out.println("lastKey = " + lastKey);
              List<Integer> lastEntry = linked.get(lastKey);
              System.out.println("lastEntry = " + lastEntry);



           }

           private static String getLastKey(Map<String, List<Integer>> linked) {
              int index = 0;
              for (String key : linked.keySet()) {
             index++;
             if (index == linked.size()) {
                return key;
             }
              }
              return null;
           }

           private static String getFirstKey(Map<String, List<Integer>> linked) {
              for (String key : linked.keySet()) {
             return key;
              }
              return null;
           }
        }

答案 11 :(得分:0)

虽然linkedHashMap没有提供任何方法来获取first,last或任何特定对象。

但要获得它是非常微不足道的:

  • 地图orderMap = new LinkedHashMap();
    设置al = orderMap.keySet();

现在在al对象上使用iterator;你可以得到任何对象。

答案 12 :(得分:0)

是的,我遇到了同样的问题,但幸运的是我只需要第一个元素...... - 这就是我为它所做的。

private String getDefaultPlayerType()
{
    String defaultPlayerType = "";
    for(LinkedHashMap.Entry<String,Integer> entry : getLeagueByName(currentLeague).getStatisticsOrder().entrySet())
    {
        defaultPlayerType = entry.getKey();
        break;
    }
    return defaultPlayerType;
}

如果您还需要最后一个元素 - 我将研究如何反转地图的顺序 - 将其存储在临时变量中,访问反转地图中的第一个元素(因此它将是您的最后一个元素) ,杀死临时变量。

以下是关于如何反向排序散列图的一些好答案:

How to iterate hashmap in reverse order in Java

如果您使用上述链接中的帮助,请给予他们投票:) 希望这可以帮助别人。

答案 13 :(得分:0)

对,您必须手动枚举密钥集,直到链表的末尾,然后通过密钥检索条目并返回此条目。

答案 14 :(得分:0)

public static List<Fragment> pullToBackStack() {
    List<Fragment> fragments = new ArrayList<>();
    List<Map.Entry<String, Fragment>> entryList = new ArrayList<>(backMap.entrySet());
    int size = entryList.size();
    if (size > 0) {
        for (int i = size - 1; i >= 0; i--) {// last Fragments
            fragments.add(entryList.get(i).getValue());
            backMap.remove(entryList.get(i).getKey());
        }
        return fragments;
    }
    return null;
}