我有这样的任务:在字符串的排序流中,将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}
我可以在流中计数相同的字符串,但我不知道如何替换组,无额外迭代
如果您将继续使用流方法,那么您别无选择,只能流经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())
收集到列表,如果您看到“三元组”,则快退。
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
被意识到“三元组”可以跨越两个列表的事物替换为止。