在Java 8流中对组进行分组和替换

时间:2019-10-28 19:34:37

标签: java java-8 java-stream grouping collectors

我有这样的任务:在排序的字符串流中,将3个相同字符串的所有组更改为一个包含大写字母的字符串(使用Java 8流API)。示例:

input = {"a","a","b","b","b","c"} 

output = {"a","a","B","c"}

我可以在流中计算相同的字符串,但是我不理解如何在没有额外迭代的情况下替换组。我现在所拥有的是:

Map<String, Long> result = Stream.of("a","a","b","b","b","c")
            .collect(Collectors.groupingBy(Function.identity(), 
                    LinkedHashMap::new, Collectors.counting()));

System.out.println(result);

//当前输出:{a = 2,b = 3,c = 1}

2 个答案:

答案 0 :(得分:0)

  

我可以在流中计数相同的字符串,但是我不知道如何   替换组,没有额外的迭代次数

如果您要坚持使用流方法,那么别无选择,只能流遍entrySet()

我要指出的第二件事是,与其使用counting收集器,不如使用toList收集器,这样会使流转时的生活更加轻松entrySet执行进一步的操作。

 Stream.of("a", "a", "b", "b", "b", "c")
       .collect(groupingBy(Function.identity(),
                        LinkedHashMap::new,
                        toList()))
       .entrySet().stream()
       .flatMap(e -> e.getValue().size() == 3 ? Stream.of(e.getKey().toUpperCase()) :
             e.getValue().stream())
       .collect(toList());

出于完整性考虑,如果您要坚持使用counting收集器,则可以执行以下操作:

Stream.of("a", "a", "b", "b", "b", "c")
      .collect(groupingBy(Function.identity(),
                        LinkedHashMap::new,
                        counting()))
      .entrySet().stream()
      .flatMap(e -> e.getValue() == 3 ? Stream.of(e.getKey().toUpperCase()) :
                        Stream.generate(e::getKey).limit(e.getValue()))
      .collect(Collectors.toList());

如果您想...也可以将Stream.generate(e::getKey).limit(e.getValue())替换为LongStream.range(0, e.getValue()).mapToObj(s -> e.getKey())

答案 1 :(得分:0)

收集到一个列表,如果“看到一个三元组”,则快退。

List<String> coalesced = Stream.of("a", "a", "b", "b", "b", "c")
  .sequential()
  .collect(LinkedList::new, this::coalesce, List::addAll);
System.out.println(coalesced);

private void coalesce(LinkedList<String> list, String s) {
  if (s.equals(list.peekLast()) &&
      list.size() > 1 &&
      s.equals(list.get(list.size() - 2))) {
    list.removeLast();
    list.removeLast();
    list.add(s.toUpperCase());
  } else {
    list.add(s);
  }
}

作为收集器,这是线程安全的,尽管以下内容仅对单线程流有效,直到List::addAll被知道“三元组”可以跨越两个列表。