SortedMap
代替 NavigableMap
吗? (NavigableMap
自1.6以来才存在;SortedMap
自1.2以来一直存在)
我正在尝试找到具有最大键的值,例如键<= reference key K0. I can't seem to figure out how to do this with a
SortedMap
(如果严格来说是<, then I'd call headMap()
,然后是lastKey()
,然后是get()
),但是NavigableMap.floorEntry()
似乎正是我需要。
澄清:仅作为示例,我正在使用不同的行为模型处理稀疏范围的版本号。键可能是 [0, 2, 5],因此版本号 0 和 1 由键 #0 处的值处理,版本号 2-4 由键 #2 处的值处理,版本号 >= 5由键 #5 处的值处理。
就我个人而言,我坚信使用最不具体的界面来为您提供所需的内容。这使您的意图更加清晰,并对您可能的实现施加更少的限制。
大多数开发人员希望排序集合用于迭代目的,或许还为了随机访问性能。我见过很少需要闭合元素的情况。
如果您需要该功能,请继续。我认为 TreeMap 实际上实现了 NavigableMap。但当你不需要它时,为什么要限制自己呢?
除了 JVM 版本之外,还有什么理由使用 SortedMap 而不是 NavigableMap 吗?
是的,我能想到一个例子。地图的提供者可能已经用
Collections.unmodifiableSortedMap
包装了它,因此即使源是 TreeMap
(实现 NavigableMap
),您也只能引用 SortedMap
并且不能将其转换为 NavigableMap
.
我正在尝试找到具有最大键的值,例如该键<= reference key K0. I can't seem to figure out how to do this with a SortedMap
有两种情况:要么映射包含与键完全匹配的内容,要么不包含。因此,首先查找完全匹配,只有当它不存在时,
m.headMap(key).lastKey()
才会给出正确的答案。
这样就可以了(尽管它不如真正的
NavigableMap
那么有效):
static <K, V> Map.Entry<K, V> floorEntry(final SortedMap<K, V> m, K key) {
final SortedMap<K, V> tail;
if (m.containsKey(key)) {
tail = m.tailMap(key);
} else {
SortedMap<K, V> head = m.headMap(key);
if (head.isEmpty()) {
return null;
} else {
tail = head.tailMap(head.lastKey());
}
}
return tail.entrySet()
.iterator()
.next();
}
除了 JVM 版本之外,还有什么理由使用
代替SortedMap
吗?NavigableMap
是的,如果您需要在 Java 7 或更早版本上返回不可修改的地图,并且您没有使用 Google Guava。
NavigableMap
旨在取代 SortedMap
。 NavigableMap
向SortedMap
接口添加经常需要的方法,对于Map
实现者来说很容易添加,但用现有的SortedMap
方法编写却很尴尬。返回 SortedMap
而不是 NavigableMap
将导致代码的调用者进行不必要的工作。
不幸的是没有提供
Collections.unmodifiableNavigableMap
。 IMO 这可能是一个疏忽,但它在 JDK 1.7 中没有得到纠正,所以也许有人有理由忽略它。然而,它是在 Java 8 中添加的。我建议使用com.google.common.collect.Maps.unmodifiableNavigableMap
。
使用整数时,您可以使用 x < A+1 instead of x <= A and you're done. I mean headMap(A+1), etc., should do the job. Otherwise, I'd go for finnw's solution since it's more clear than anything I can come out with.
我认为使用Iterators比使用headMap和tailMap更好,因为效率不高。我测试了以下代码的情况,它运行良好,并且 FloorEntry2 比 FloorEntry1 快三倍。
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.*;
import java.util.Map.Entry;
import org.junit.Before;
import org.junit.Test;
class TestMapUtils <K,V> {
public TestMapUtils()
{
}
public Map.Entry<K, V> floorEntry1(final SortedMap<K, V> m, K key) {
final SortedMap<K, V> tail;
if (m.containsKey(key)) {
tail = m.tailMap(key);
} else {
SortedMap<K, V> head = m.headMap(key);
if (head.isEmpty()) {
return null;
} else {
tail = head.tailMap(head.lastKey());
}
}
return tail.entrySet()
.iterator()
.next();
}
public Map.Entry<K, V> floorEntry2(final NavigableMap<K, V> m, K key) {
Iterator<Map.Entry<K,V>> i = m.entrySet().iterator();
Entry<K,V> tailEntry = null;
if (key==null) {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (e.getKey()==null)
return null;
}
} else {
while (i.hasNext()) {
Entry<K,V> e = i.next();
if (key.equals(e.getKey()))
{
return e;
}
else
{
if (((Integer) e.getKey()).intValue() < (((Integer) key).intValue())) {
tailEntry = e;
}
}
}
}
return tailEntry;
}
}
public class TestSortedMap {
protected TestMapUtils<Integer, Integer> testMapUtils;
private NavigableMap<Integer, Integer> sortedMap;
@Before
public void setUp()
{
testMapUtils = new TestMapUtils<Integer, Integer>();
sortedMap = addElementsToMap();
}
private NavigableMap<Integer, Integer> addElementsToMap() {
NavigableMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
map.put(10, 1);
map.put(20, 2);
map.put(30, 3);
map.put(40, 4);
map.put(50, 5);
map.put(60, 6);
return map;
}
@Test
public void testFloorEntry()
{
long startTime1 = System.nanoTime();
Entry<Integer, Integer> entry = testMapUtils.floorEntry2(sortedMap, 30);
assertNotNull(entry);
assertEquals(entry.getKey().intValue(), 30);
entry = testMapUtils.floorEntry2(sortedMap, 60);
assertNotNull(entry);
assertEquals(entry.getKey().intValue(), 60);
entry = testMapUtils.floorEntry2(sortedMap, 70);
assertNotNull(entry);
assertEquals(entry.getKey().intValue(), 60);
entry = testMapUtils.floorEntry2(sortedMap, 55);
assertNotNull(entry);
assertEquals(entry.getKey().intValue(), 50);
entry = testMapUtils.floorEntry2(sortedMap, 31);
assertNotNull(entry);
assertEquals(entry.getKey().intValue(), 30);
entry = testMapUtils.floorEntry2(sortedMap, 0);
assertNull(entry);
long endTime1 = System.nanoTime() - startTime1;
long startTime2 = System.nanoTime();
entry = testMapUtils.floorEntry1(sortedMap, 30);
assertNotNull(entry);
assertEquals(entry.getKey().intValue(), 30);
entry = testMapUtils.floorEntry1(sortedMap, 60);
assertNotNull(entry);
assertEquals(entry.getKey().intValue(), 60);
entry = testMapUtils.floorEntry1(sortedMap, 70);
assertNotNull(entry);
assertEquals(entry.getKey().intValue(), 60);
entry = testMapUtils.floorEntry1(sortedMap, 55);
assertNotNull(entry);
assertEquals(entry.getKey().intValue(), 50);
entry = testMapUtils.floorEntry1(sortedMap, 31);
assertNotNull(entry);
assertEquals(entry.getKey().intValue(), 30);
entry = testMapUtils.floorEntry1(sortedMap, 0);
assertNull(entry);
long endTime2 = System.nanoTime() - startTime2;
if ( endTime2 > endTime1 )
{
System.out.println("First Execution is faster.. "+endTime1+", "+endTime2);
} else if ( endTime1 > endTime2 ) {
System.out.println("Second Execution is faster.. "+endTime1+", "+endTime2);
} else {
System.out.println("Execution times are same");
}
}
}
“[NavigableMap] 旨在取代 SortedMap 接口。” http://download.oracle.com/javase/6/docs/technotes/guides/collections/changes6.html
尽管如此,我还是同意 Uri 的观点:使用尽可能不具体的接口。