我使用了LinkedHashMap
,因为在该映射表中,键的输入顺序很重要。
但现在,我想得到第一个(最先输入的)或最后一个键的值。
是否需要像first()
和last()
这样的方法?
我是否需要一个迭代器来获取第一个键入口?这就是我使用LinkedHashMap
的原因!
谢谢!
我使用了LinkedHashMap
,因为在该映射表中,键的输入顺序很重要。
但现在,我想得到第一个(最先输入的)或最后一个键的值。
是否需要像first()
和last()
这样的方法?
我是否需要一个迭代器来获取第一个键入口?这就是我使用LinkedHashMap
的原因!
谢谢!
LinkedHashMap
的语义仍然是Map,而不是LinkedList。它保留插入顺序,但这是实现细节,而不是其接口的一部分。获得“第一个”条目的最快方法仍然是entrySet().iterator().next()
。获取“最后一个”条目是可能的,但需要通过调用.next()
迭代整个条目集来实现,直到到达最后一个。 while(iterator.hasNext()) { lastElement = iterator.next() }
编辑:但是,如果您愿意超越JavaSE API,Apache Commons Collections有自己的LinkedMap
实现,具有像firstKey
和lastKey
这样的方法,可以满足您的需求。界面更加丰富。mylinkedmap.entrySet().iterator().next()
的时间复杂度是多少?是O(1)吗? - tkrishtop你可以尝试类似这样的方法(获取最后一个条目):
linkedHashMap.entrySet().toArray()[linkedHashMap.size() -1];
T last = null ; for( T item : linkedHashMap.values() ) last = item;
尽管时间复杂度为O(N),但空间复杂度为O(1)。 - Florian F我知道我来晚了,但我想提供一些替代方案,不是什么特别的东西,而是一些在这里没有提到的情况。如果有人不太在意效率,但希望拥有更简单的东西(也许可以用一行代码找到最后一个条目值),所有这些都将随着 Java 8 的到来而变得更加简化。我提供了一些有用的场景。
为了完整起见,我将这些替代方案与其他用户在此帖子中已经提到的数组解决方案进行了比较。我总结了所有情况,并认为它们会很有用(无论是否需要性能),特别是对于新开发人员,这总取决于每个问题的实质。
我从先前回答中采用了这个解决方案,以进行以下比较。这个解决方案属于@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();
}
通过跳过该元素之前的所有元素,此方法将获取流的最后一个元素。
public static String FindLasstEntryWithSkipFunctionMethod() {
final long count = linkedmap.entrySet().stream().count();
return linkedmap.entrySet().stream().skip(count - 1).findFirst().get().getValue();
}
来自Google Guava的Iterables.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
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);
}
catch
中添加ClassCastException
,以防tail
不是子类(或未来实现)中的Entry
。 - Paul Boddington获取LinkedHashMap的第一个和最后一个条目的另一种方法是使用Set接口的toArray()
方法。
但我认为迭代entry set并获取第一个和最后一个条目是更好的方法。
使用数组方法会导致警告形式的"...需要未经检查的转换以符合...",这无法修复[但可以通过使用注释@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
虽然有点不太规范,但你可以重写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);
}
然而,当你开始删除条目时,一切都会破碎;我还没有想出一种解决方法。
很让人恼火的是,除此之外,你无法以明智的方式访问头部或尾部...
可能像这样:
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;
}
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();
firstEntry()
和lastEntry()
方法轻松访问。LinkedHashMap<Integer, String> linkedHashMap = // ...
Entry<Integer, String> firstEntry = linkedHashMap.firstEntry();
Entry<Integer, String> lastValue = linkedHashMap.lastEntry();
Integer firstKey = firstEntry.getKey();
Integer lastKey = lastEntry.getKey();
String firstValue = firstEntry.getValue();
String lastValue = lastEntry.getValue();
建议:
map.remove(map.keySet().iterator().next());
LinkedHashMap
没有实现Deque
。它完全可以这样做。 - forresthopkinsa