有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

lambda Java流采集如何推断类型?

我得到了一个单词流Stream<String> words,以及一个类Pair<String,Integer>,它使用getter和setter方法为(someString, someInt)实现了一个简单的元组,用于两个名为getFirst、setFirst、getSecond和setSecond的元素

现在,我应该将流中的每个单词组合成一对(word, 1),然后使用收集器以某种方式让整个过程告诉我每个单词在文本中的频率。现在我已经找到了一个应该让我做我想做的事情的收集器,并将其作为。收集(…)去小溪边
但是整个事情看起来很复杂,在这个主题中出现的类型推断、演绎和通配符并没有使它变得更容易,所以我现在没有任何线索,只是知道我创建了什么

我试着从API中推断出来,并尝试了所有我能想到的东西,但没有一个与之匹配:

words
  .map(x -> new Pair<String,Integer>(x,1))
  .collect(Collectors.groupingBy(
      x -> x.getFirst(),
      Collectors.reducing(
          (a,b) -> new Pair<String,Integer>(a.getFirst(), a.getSecond() + b.getSecond())
      )
   ));

共 (3) 个答案

  1. # 1 楼答案

    我同意一开始进入收集器的世界可能有点可怕,特别是当您需要处理泛型类型参数时

    有很多方法可以解决您的问题,无论是使用流还是不使用流

    使用流:

    Map<String, Pair<String, Integer>> map = words.stream()
        .collect(Collectors.toMap(
            word -> word, 
            word -> new Pair<>(word, 1),
            (o, n) -> {
                o.setSecond(o.getSecond() + n.getSecond()); 
                return o;
            }));
    
    Collection<Pair<String, Integer>> result = map.values();
    

    ^{}通过将流的每个元素转换为键来工作(这是第一个参数word -> word,这意味着我们保持单词原样,以便它将成为映射的键),通过将流的每个元素转换为值(这是第二个参数word -> new Pair<>(word, 1),这意味着我们第一次找到了这个单词,因此我们为这个单词创建了一个新的Pair实例,其计数为1

    第三个参数是一个合并函数,当第一个参数返回已属于映射的键时,该函数用于合并值。由于映射对于同一个键不能有多个条目,因此我们需要一种方法来合并映射中已存在的该键的值,并使用第二个参数生成的新值。在这种情况下,o代表旧值,n代表新值。合并值的方法是将单词的计数相加,并在Pair实例中设置与旧值对应的新计数。无需使用单词和新计数创建一个新的Pair实例,因为通过对^{的旧实例进行变异来累积计数是安全的

    没有溪流:

    Map<String, Pair<String, Integer>> map = new HashMap<>();
    words.forEach(word -> map.merge(
        word, 
        new Pair<>(word, 1),
        (o, n) -> {
            o.setSecond(o.getSecond() + n.getSecond()); 
            return o;
        }));
    
    Collection<Pair<String, Integer>> result = map.values();
    

    它使用^{},并且与前面的代码具有类似的语义

  2. # 2 楼答案

    最简单(但不一定是最有效)的解决方案是将条目分组到一个映射,然后将其转换为成对:

    List<Pair<String, Integer>> pairs = words
            .collect(Collectors.groupingBy(x -> x, Collectors.summingInt(x -> 1)))
            .entrySet()
            .stream()
            .map(e -> new Pair(e.getKey(), e.getValue()))
            .collect(Collectors.toList());
    
  3. # 3 楼答案

    尝试使用^{}

    Collection<Pair<String, Integer>> values = words.collect(Collectors.toMap(
          Function.identity(),
          s -> new Pair<>(s, 1),
          (a, b) -> {a.setSecond(a.getSecond() + b.getSecond()); return a;}
    )).values();
    

    它将使用提供的以下命令从您的流创建地图:

    • keyMapper-用于生成键的映射函数
    • valueMapper-用于生成值的映射函数
    • mergeFunction-一个合并函数,用于解决与同一键关联的值之间的冲突

    因此,它通过字符串值将对分组到映射中,然后您只需调用.values()即可获得对的集合