【问题标题】:How to get nth item from a TreeMap?如何从 TreeMap 中获取第 n 个项目?
【发布时间】:2019-03-23 15:57:02
【问题描述】:

我有一个TreeMap<Date, Integer>,我想从该地图中获取第 n 个项目。我现在想出的是这样的:

((Integer)myTreeMap.values().toArray()[index]).intValue();

但这感觉很笨拙,更不用说堆上或性能方面发生了什么?

有没有一种简洁的方法可以从TreeMap/SortedMap 中获取第n项?

【问题讨论】:

  • 为什么不在树图值上使用流并跳过 n-1 个元素并获取其余元素中的第一个?
  • 除非您有自定义实现,否则迭代是知道哪个叶节点是“第 n 个”的唯一方法。
  • 默认的 TreeMap 实现不支持。它使用内部java.util.TreeMap.Values 类型来保留不支持基于索引的访问的值。对于键 Set 也是如此。它们确实使获取高于/低于阈值的键值变得容易,也许您可​​以尝试使用它而不是第 n 个值?

标签: java treemap sortedmap


【解决方案1】:

编辑:另一种选择是

final Optional<T> first =
        treeMap.values()
               .stream()
               .skip(index - 1)
               .findFirst();

if (first.isPresent()) {
    final T value = first.get();
}

怎么样

final List<T> values = new ArrayList<>(myTreeMap.values());
final T value = values.get(index);

T 只是代替您的 Map 值类型的泛型类型。
index 是您的 nth 元素。

正如所指出的,这对于大型地图来说效率很低,因为在内部调用 ArrayList 构造函数 toArray 方法

public Object[] toArray() {
    // Estimate size of array; be prepared to see more or fewer elements
    Object[] r = new Object[size()];
    Iterator<E> it = iterator();
    for (int i = 0; i < r.length; i++) {
        if (! it.hasNext()) // fewer elements than expected
            return Arrays.copyOf(r, i);
        r[i] = it.next();
    }
    return it.hasNext() ? finishToArray(r, it) : r;
}

【讨论】:

  • 如果地图很大,这将非常低效......您实例化所有值的完整列表只是为了获得可能真正接近“头部”的单个值。
  • @Renato 同样适用于 Stream 版本,我认为使用 Collect.toList。
  • 一点也不。流版本不需要收集完整的集合(它甚至不需要收集,这个答案需要改进)。
  • @Renato 是的,但是您必须在跳过后使用 findFirst。使用 Collectors.toList 您需要完整列表。
【解决方案2】:

您可以使用 steam 并跳过 n-1 个元素,然后取第一个元素,如下所示: tmap.entrySet().stream().skip(n-1).findFirst();。数据集示例的详细信息:

TreeMap<Date, Integer> tmap = new TreeMap<Date, Integer>();
tmap.put(new Date(2014, 1, 1), 0);
tmap.put(new Date(2015, 1, 1), 1);
tmap.put(new Date(2016, 1, 1), 2);
tmap.put(new Date(2017, 1, 1), 3);
tmap.put(new Date(2018, 1,1 ), 4);

System.out.println(tmap);

// Let's find the nth elements i.e n = 3;
int n = 3;
System.out.println(" " + n + " elements: ");
System.out.println(tmap.entrySet().stream().skip(n-1).findFirst());

输出如下:

{Sun Feb 01 00:00:00 IST 3914=0, Mon Feb 01 00:00:00 IST 3915=1, Tue Feb 01 00:00:00 IST 3916=2, Thu Feb 01 00:00:00 IST 3917=3, Fri Feb 01 00:00:00 IST 3918=4}
3 elements: 
Optional[Tue Feb 01 00:00:00 IST 3916=2]

【讨论】:

    【解决方案3】:

    我们可以在这里尝试使用流。在下面的 sn-p 中,我们创建了一个包含五个条目的排序映射。然后,我们创建一个流,跳过前 3 个元素,并使用findFirst 捕获第一个元素。

    Map<Integer, String> myTreeMap = new TreeMap<>();
    myTreeMap.put(1, "one");
    myTreeMap.put(2, "two");
    myTreeMap.put(3, "three");
    myTreeMap.put(4, "four");
    myTreeMap.put(5, "five");
    String fourth = myTreeMap.entrySet().stream()
       .skip(3)
       .map(map -> map.getValue()).findFirst().get();
    System.out.println("fourth value in map is: " + fourth);
    

    【讨论】:

    • 当您可以使用这些值并且只需要它们时,为什么还要流式传输条目集?
    • 随便treeMap.values().stream().skip(5).findFirst().orElse(-1)
    • @mayamar 该地图可能在内部将其数据维护为Entrys;调用 values() 将强制它首先完全解决那些创建该集合的问题。所以这取决于实现,但效率更高的可能性更高。
    • @daniu 不,我检查过,两者都调用“getFirstEntry()”,并且都使用从那里开始的迭代器
    猜你喜欢
    • 1970-01-01
    • 2015-11-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-24
    • 1970-01-01
    • 1970-01-01
    • 2015-09-22
    相关资源
    最近更新 更多