收集结果
可以调用iterator
方法,查看流中的元素。
Iterator<String> iterator = stream.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
可以调用forEach
方法,将某个函数应用与每个元素
stream.forEach(System.out::println);
在并行流上,forEach
方法会以任意顺序遍历各个元素。如果像按照流中顺序来处理,可以调用forEachOrdered
方法。
stream.forEachOrdered(item -> System.out.println(item));
这个方法会丧失并行处理的部分甚至全部优势。
可以调用toArray
,获得由流的元素构造的数组。stream.toArray()
会返回一个Object[]
数组。
Object[] objects = stream.toArray();
想要让数组具有正确的类型,可以将其传递到数组构造器中:
String[] array = stream.toArray(String[]::new);
针对将流中的元素收集到另一个目标中,可用collect
方法,它接受一个Collector
接口的实例。
List<String> list = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
要控制获得的集的种类,那么可以使用下面的调用:
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));
TreeSet<String> treeSet = stream.collect(Collectors.toCollection(TreeSet::new));
想要通过链接操作来收集流中的所有字符串。可以调用:
String collect = stream.collect(Collectors.joining(","));
想要将流的结果约简为总和、平均值、最大值或最小值,可以使用summarizing(int|long|double)
方法中的某一个。
这些方法会接受一个将流对象映射为数据的函数,同时,产生类型为(int|long|double)SummaryStatistic
的结果,同时算总和、数量、平均值、最大值和最小值。
IntSummaryStatistics summaryStatistics = stream.collect(Collectors.summarizingInt(String::length));
long sum = summaryStatistics.getSum();
long count = summaryStatistics.getCount();
summaryStatistics.getAverage();
summaryStatistics.getMax();
summaryStatistics.getMin();
收集到映射表中
Collectors.toMap
方法用来将流中元素收集到一个映射表中。
Map<Integer, String> idToName = people.collect(Collectors.toMap(Person::getId, Person::getName));
Map<Integer, String> idToName = people.collect(Collectors.toMap(Person::getId, Function.identity()));
@FunctionalInterface
public interface Function<T, R> {
/**
* Returns a function that always returns its input argument.
*
* @param <T> the type of the input and output objects to the function
* @return a function that always returns its input argument
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
如果有多个元素具有相同的键,那么就会存在冲突,收集器将抛出一个IllegalStateException
对象。
可以通过提供第3个函数引元来覆盖这种行为,该函数会针对给定的已有值和新值来解决冲突并确定键对应的值。
Stream<Local> locales = Stream.of(Locale.getAvaliableLocales());
Map<String, String> languageNames = locales.collect(
Collectors.toMap(
Locale::getDisplayLanguage,
l -> l.getDisplayLanguage(l),
(existingValue, newValue) -> existingValue));
如果想要得到TreeMap
,可以将构造器作为第4个引元来提供。且必须提供一个哈并函数。
Map<Integer, Person> idToPerson = people.collect(
Collectors.toMap(
Person::getId,
Function.identity(),
(existingValue, newValue) -> { throw new IllegalStateException(); }
TreeMap::new));
对于每个toMap
方法,都有一个等价的可以产生并发映射表的toConcurrentMap
方法。
群组和分区
groupingBy
方法支持将具有相同特性的值群聚成组。
Map<String, List<Locale>> countryToLocales = locales.collect(
Collectors.groupingBy(Locale::getCountry));
List<Locale> swissLocales = countryToLocales.get("CH");
当分类函数是断言函数(即返回boolean值的函数)时,流的元素别分为两个列表:该函数返回true的元素和其他元素。这种情况下,使用partitioningBy
比使用groupingBy
要更高效。
Map<Boolea, List<Locale>> englishAndOtherLocales = locales.collect(
Collectors.partitionBy(l -> l.getLanguage().equals("en");
下游收集器
groupingBy
方法会产生一个映射表,它的每个值都是一个列表。想要以某种方式来处理这些列表,就需要提供一个“下游收集器”。
Map<String, Set<Locale>> countryToLocaleSet = locales.collect(
groupingBy(Locale::getCountry, Collector.toSet();
Java提供了多种可以将群组元素约简为数字的收集器:
Map<String, Long> countryToLocaleCounts = locales.collect(
groupingBy(Locale::getCountry, counting());
Map<String, Integer> stateToCityPopulation = cities.collect(
groupingBy(City::getState, summingInt(City::getPopulation)));
Map<String, Optional<City>> stateToLargestCity = cities.collect(
groupingBy(City::getState, maxBy(Comparator.comparing(City::getPopulation))));
Map<String, Optional<String>> stateToLongestCityName = cities.collect(
groupingBy(City::getState, mapping(City::getName, maxBy(Comparator.comparing(String::length)))));
Map<String, Set<String>> countryToLanguages = locales.collect(
groupingBy(Locale::getDisplayCountry, mapping(Locale::getDisplayLanguage, toSet())));
Map<String, IntSummaryStatistics> stateToCityPopulationSummary = cities.collect(groupingBy(City::getState, summarizingInt(City::getPopulation)));
约简操作
reduce
方法是一种用于从流中计算某个值的通用机制,其最简单形式将接受一个二元函数,并从前两个元素开始持续应用它。
List<Integer> values = ...;
Optional<Integer> sum = values.strema().reduce(((x, y) -> x + y);