JavaLinkedHashMap 获取第一个或最后一个条目

我使用 LinkedHashMap是因为键在地图中输入的顺序很重要。

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

应该有像 first()last()这样的方法吗?

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

谢谢!

169037 次浏览

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

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

编辑 : 然而,如果您愿意超越 JavaSE API,那么 Apache Commons 集合有自己的 LinkedMap实现,其中包含诸如 firstKeylastKey之类的方法,这些方法可以完成您所需要的工作。界面相当丰富。

获得 LinkedHashMap 的第一个和最后一个条目的另一种方法是使用 Set 接口的 toArray()方法。

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

数组方法的使用导致对形式 “ ... 需要不受限制的转换,以符合...”的警告,该形式无法修复[但只能通过使用注释 @SuppressWarnings("unchecked")]来抑制。

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

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

尽管 linkedHashMap 没有提供任何方法来获取第一个、最后一个或任何特定的对象。

但要得到这样的结果相当微不足道:

Map<Integer,String> orderMap = new LinkedHashMap<Integer,String>();
Set<Integer> al =   orderMap.keySet();

现在对 al对象使用迭代器; 您可以获取任何对象。

也许是这样的:

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

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

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

如果你也需要最后一个元素——我会研究如何反转映射的顺序——将它存储在一个临时变量中,访问反向映射中的第一个元素(因此它将是你的最后一个元素) ,杀死临时变量。

下面是一些关于如何对散列表进行反向排序的好答案:

如何在 Java 中反向迭代散列表

如果您使用上述链接的帮助,请给他们投赞成票:) 希望这个能帮到别人。

LinkedHashMap当前实现(Java8)跟踪其尾部。如果性能是一个问题,并且/或者映射的大小很大,您可以通过反射访问该字段。

因为实现可能会发生变化,所以有一个备用策略可能也是一个好主意。如果抛出异常,您可能需要记录一些内容,以便知道实现已更改。

它可能看起来像:

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

它有点脏,但是您可以覆盖 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);
}

但是,当您开始删除条目时,所有这些都会崩溃; 还没有找到一种方法来组装这些条目。

这是非常恼人的,否则你不能得到访问头或尾在一个合理的方式..。

建议:

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

你能不能试试这样做(得到最后一个条目) :

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

我知道我来得太晚了,但我想提供一些替代方案,不是什么特别的方案,而是这里没有提到的一些案例。如果有人不太关心效率,但是他想要更简单的东西(可能用一行代码找到最后一个条目值) ,那么随着 我提供了一些有用的场景。

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

可能的替代方案

数组方法的使用

我从前面的答案中提取出来进行下面的比较,这个解是@feresr。

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

数组列表方法的使用

与第一种解决方案类似,但性能稍有不同

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 方法

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

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

可迭代替代方案

来自 Google Guava 的 Iterable.getLast 还有列表和排序集

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

对,你必须手动枚举键集直到链接列表的末尾,然后按键检索条目并返回这个条目。

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

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

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

使用 Java8流可以很容易地做到这一点:

LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("A", 1);
linkedHashMap.put("B", 2);
linkedHashMap.put("C", 3);
linkedHashMap.put("D", 4);


//First entry
Map.Entry<String, Integer> firstEntry = linkedHashMap.entrySet().stream().findFirst().get();


//Last entry
Map.Entry<String, Integer> lastEntry = linkedHashMap.entrySet().stream().skip(linkedHashMap.size() - 1).findFirst().get();