如何使用 Java Streams API 添加和更新地图条目
Posted
技术标签:
【中文标题】如何使用 Java Streams API 添加和更新地图条目【英文标题】:How to add and update map entry using Java Streams API 【发布时间】:2021-12-19 04:24:37 【问题描述】:我刚开始学习 Streams,我的任务是对某个字符串数组中的所有单词进行计数和排序。我已经将输入解析为单词,但我不知道如何使用流添加和更新条目。
有我的解析流:
Stream<String> stringStream = lines.stream().flatMap(s -> Arrays.stream(s.split("[^a-zA-Z]+")));
String[] parsed = stringStream.toArray(String[]::new);
我在没有流的情况下完成了这项任务,就像这样:
Map<String,WordStatistics> wordToFrequencyMap = new HashMap<>();
for (String line: lines)
line=line.toLowerCase();
String[] mas = line.split("[^a-zA-Z]+");
for (String word:mas)
if(word.length()>3)
if (!wordToFrequencyMap.containsKey(word))
wordToFrequencyMap.put(word, new WordStatistics(word, 1));
else
WordStatistics tmp = wordToFrequencyMap.get(word);
tmp.setFreq(tmp.getFreq() + 1);
WordStatistics 类:
public class WordStatistics implements Comparable<WordStatistics>
private String word;
private int freq;
public WordStatistics(String word, int freq)
this.word = word;
this.freq = freq;
public String getWord()
return word;
public int getFreq()
return freq;
public void setWord(String word)
this.word = word;
public void setFreq(int freq)
this.freq = freq;
@Override
public int compareTo(WordStatistics o)
if(this.freq > o.freq)
return 1;
if(this.freq == o.freq)
return -this.word.compareTo(o.word);
return -1;
【问题讨论】:
这个WordStatistics
类有什么好处? Map<String,Integer>
或 Map<String,Long>
确实已经充分描述了词频。
@Holger 我还需要按频率对我的条目进行排序,然后在需要时按字母顺序(如果单词具有相同的频率),因此我决定将频率和单词存储在一起。我在compareTo
方法中有这个比较。
您无法对Map
进行排序,因此您的排序操作无论如何都必须将数据存储到一个新集合中,而这又不需要此类,例如lines.stream() .flatMap(Pattern.compile ( "[^a-zA-Z]+")::splitAsStream) .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) .entrySet().stream() .sorted(Map.Entry.<String,Long>comparingByValue().reversed().thenComparing(Map.Entry.comparingByKey())) .forEachOrdered(System.out::println);
不实现自定义 compareTo
方法可以防止您犯错误,例如使用减号(减号可以溢出)来反转。
【参考方案1】:
一种简单的方法是使用合并功能收集toMap()
:
Map<String, WordStatistics> wordToFrequencyMap = lines.stream()
.map(s -> s.split("[^a-zA-Z]+"))
.flatMap(Arrays::stream)
.collect(Collectors.toMap(w -> w, w -> new WordStatistics(w, 1), (ws1, ws2) ->
ws1.setFreq(ws1.getFreq() + ws2.getFreq());
return ws1;
));
分解toMap()
参数:
w -> w
只是表示使用流元素作为映射键。
下一个参数为键生成一个值,该值最初是 WordStatistics
的新实例,频率为 1
。
最后,我们告诉收集器如何将属于同一个键的值合并在一起。在我们的例子中,我们将频率相加为一个值 (ws1
) 并将其作为合并结果返回。
【讨论】:
谢谢,它确实很好用!有一件事我想不通,你能澄清一下这段代码是如何工作的,尤其是 lambda 的那部分吗?.collect(Collectors.toMap(w -> w, w -> new WordStatistics(w, 1), (ws1, ws2) -> ws1.setFreq(ws1.getFreq() + ws2.getFreq());
@Wtht.Nws 添加了一些解释。有关详细信息,请参阅链接的 Javadoc。【参考方案2】:
这应该与您现在在循环中所做的几乎相同。
Pattern pattern = Pattern.compile("[^a-zA-Z]+");
lines.stream().flatMap(pattern::splitAsStream).filter(s -> s.length() > 3).forEach(s ->
WordStatistics tmp = wordToFrequencyMap.get(s);
if (tmp == null)
wordToFrequencyMap.put(s, new WordStatistics(word, 1));
else
tmp.setFreq(tmp.getFreq() + 1);
);
【讨论】:
以上是关于如何使用 Java Streams API 添加和更新地图条目的主要内容,如果未能解决你的问题,请参考以下文章