在java中查找(树)映射中的所有最大值以及流和lambda



所以我必须在java中的带有流和lambda的映射中找到最大值。找到一个最大值不是问题,但如何找到多个?示例:树映射<字符串,整数>其中元素";e〃=2;i〃=1;a〃=2.我目前的解决方案给了我";a〃=2,但是我想要";a〃=2;e〃=2

我的代码:

Map<String, Integer> frequencies = new Treemap<>();
frequencies.put("e", 2);//I don't put the values in like this but it'll do to test
frequencies.put("i", 1);
frequencies.put("a", 2);
Optional<Map.Entry<String, Integer>> maxEntry = frequencies.entrySet().stream()
.max(Map.Entry.comparingByValue());//frequencies is the TreeMap<String, Integer>
//I know this only searches one max value, here's my other attempt:
try (Stream<Map.Entry<String, Integer>> stream = frequencies.entrySet().stream()) {
stream
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (k, v) -> k, LinkedHashMap::new));
//I don't know what to do here, I somehow would have to get the result into a new list, but it still only returns one result
}

如果我做错了什么,请告诉我。

所以我必须在java中的一个带有流和lambda的映射中找到最大值。

这里有一种不用树映射的方法。它对包含条目进行频率计数。

Map<String, Integer> map = Map.of("z", -1, "b", 0, "r", -2,
"s", 0, "j", 1, "a", 2, "i", 1, "e", 2);
Optional<Entry<Integer, List<Entry<String,Integer>>>> opt = map.entrySet()
.stream()
.collect(Collectors.groupingBy(Entry::getValue))
.entrySet().stream().max(Entry.comparingByKey());
System.out.println(opt.isPresent() ? opt.get().getValue() : "Empty List");

打印

[a=2, e=2]

为了好玩,你可以绕过初始地图,创建一个条目流。事实上,当您创建初始映射时,您正在创建Entry对象,因此这里不涉及额外的映射开销。


Builder<Entry<String, Integer>> entryStream = Stream.builder();
entryStream.add(Map.entry("b", 0));
entryStream.add(Map.entry("r", -2));
entryStream.add(Map.entry("s", 0));
entryStream.add(Map.entry("j", 1));
entryStream.add(Map.entry("a", 2));
entryStream.add(Map.entry("i", 1));
entryStream.add(Map.entry("e", 2));

在这一点上,它与以前相同,只是流已准备好调用。

Optional<Entry<Integer, List<Entry<String, Integer>>>> opt =
entryStream.build()
.collect(Collectors
.groupingBy(Entry::getValue))
.entrySet().stream()
.max(Entry.comparingByKey());

System.out.println(opt.isPresent() ? opt.get().getValue() :
"Empty List");

与之前一样打印

[a=2, e=2]

首先,找到最大频率:

Optional<Integer> maxFreqOptional = frequencies.values()
.stream()
.max(Comparator.naturalOrder());

然后,您可以收集所有以该频率为值的条目:

Integer maxFreq = maxFreqOptional.get(); // check if it's empty first
List<String> mostFrequent = frequencies.entrySet()
.stream()
.filter(entry -> entry.getValue().equals(maxFreq))
.map(Map.Entry<String, Integer>::getKey)
.collect(Collectors.toList());

您可以根据maxEntry的值进行筛选(如果存在(,以获得最大值的所有Map.Entry

List<Map.Entry<String, Integer>> res = frequencies.entrySet().stream()
.filter(e -> e.getValue().equals(maxEntry.get().getValue()))
.collect(Collectors.toList());

在线演示在这里

输出:[a=2, e=2]

您几乎做到了:

使用值(=frequency(作为关键字,使用原始关键字作为值,将入口流收集到TreeMap中。因此,使用级联的groupingBy。然后用最大的键输入:

TreeMap<Integer, List<String>> map = frequencies.entrySet().stream()
.collect(Collectors.groupingBy(
Map.Entry<String, Integer>::getValue, TreeMap::new, Collectors.toList()
));
Map.Entry<Integer, List<String>> largest = map.lastEntry();

注意:如果你的数据集很大,并且你想避免构建这个反向映射,你可能更喜欢其他建议的解决方案之一,即迭代映射两次:一次查找最大频率,然后再次查找所有对应的条目

由于您只需要与最大值匹配的条目,因此将映射的所有条目分组到反向映射中没有任何好处。您只需要丢弃所有值与最大值不匹配的条目。

以下是使用lambdas的一种简洁方法:

Integer max = frequencies.isEmpty() ? 
Integer.MIN_VALUE :
Collections.max(frequencies.values());
List<String> result = new ArrayList<>();
frequencies.forEach((k, v) -> { if (v.equals(max)) result.add(k); });

因此,在max中有最大值,而在result中有与最大值匹配的键。该算法的时间复杂度是O(n)最坏情况,不同于将条目分组的算法,后者都是O(nlogn)

流等效版本更详细:

Integer max = frequencies.values().stream()
.max(Comparator.naturalOrder())
.orElse(Integer.MIN_VALUE);
List<String> result = frequencies.entrySet().stream()
.filter(e -> e.getValue().equals(max))
.map(Map.Entry::getKey)
.collect(Collectors.toList());

最新更新