Stream流
1 Stream流的概述
在处理集合的时候,通常我们都会遍历其中的元素,然后对每一个元素进行处理,例如:统计List列表中字符长度大于8的元素个数。
int count = 0;
for(String str : list){
if(str.length() > 8)count++;
}
在java8开始,可以使用Stream流来对集合进行操作:
long count = list.stream().filter(str -> str.length() > 8).count();
使用Stream流,从方法名就可以看出代码逻辑:过滤字符长度大于8的字符并统计个数。
1.1 流的特点
- 流并不存储元素
- 流的操作不会修改数据源。filter()方法并不会将元素从数据源中删除,而是会生成一个新的流
- 流的操作是惰性的
1.2 流的使用过程
- 创建一个流:list.stream()
- 指定将初始流转换为其他流的中间操作:filter(str -> str.length() > 8)
- 应用终止操作:count()
2 Stream流的创建
2.1 通过Collection的Stream()创建流
Collection接口中含有一个Stream()方法,该方法可以通过集合创建一个流,这是比较常用的方式。
public class TestStream {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
Set<String> set = new HashSet<>();
Stream<String> setStream = set.stream();
Queue<String> queue = new ArrayDeque<>();
Stream<String> queueStream = queue.stream();
}
}
2.2 通过Stream类的静态方法创建流
-
static Stream of(T… values)
通过给定元素创建一个流
Stream<String> stream = Stream.of("Hello", "java");
-
static Stream empty()
创建一个不包含任何元素的流
Stream<Object> emptyStream = Stream.empty();
-
static Stream gengerate(Supplier s)
创建一个无限流,流的值通过反复调用方法s获得
Stream<Double> randomStream = Stream.generate(() -> Math.random());
-
static Stream iterate(T seed,UnaryOperator f)
创建一个无限流,流的第一个元素为seed,之后的每一个元素通过调用方法f并将seed作为入参得到
Stream<Integer> integerStream = Stream.iterate(100, seed -> seed + 1);
-
static Stram iterate(T seed,Predicate<? super T> hasNext,UnaryOperator f)
创建一个无限流,流的第一个元素为sedd,之后的每一个元素通过调用方法f并将seed作为入参得到,当出现第一个不满hasNext的判断时终止创建
Stream<Integer> integerStream = Stream.iterate(100,item -> item <= 200, seed -> seed + 1);
-
static Stream ofNullable(T t)
如果t为null,返回一个空流,否则返回包含t的流
Stream<String> stram = Stream.ofNullable("Java")
2.3 通过Arrays的stream()方法创建
-
static Stream stream(T[] array)
创建一个流,需要传入一个数组
Stream<String> stream = Arrays.stream(new String[]{"hello", "java"});
2.4 通过Files的lines()方法创建
-
static Stream lines(Path path)
创建一个流,流的元素就是文件中的行
Stream<String> stram = Files.lines(Paths.get("../data/HelloJava.txt"));
3 Stream流的中间操作
流的中间操作会产生一个新的流,它的元素派生自另一个流中的元素
3.1 filter–过滤
-
Stream filter (Predicate<? super T> predicate)
传入一个断言函数,返回一个包含满足断言条件的所有元素的流。
public class TestStream { public static void main(String[] args) throws IOException { Stream<String> stream = Stream.of("Hello", "java","hello","world"); // 筛选出字符长度<=4的元素 Stream<String> newStream = stream.filter(s -> s.length() <= 4); } }
3.2 map–映射
-
Stream map (Function<? super T,? extend Stream<? extend R>> mapper)
产生一个新的流,它将包含mapper应用于当前流中所有元素所产生的的结果
Stream<String> stream = Stream.of("Hello", "java","hello","world","Spring","Mysql"); // 将流中的元素变成大写 Stream<String> upStream = stream.map(s -> s.toUpperCase()); // 得到每个元素的长度的流 Stream<Integer> lengthStream = stream.map(s -> s.length());
3.3 flatMap–摊平
-
Stream flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
产生一个流,它是通过将mapper应用于当前流中所有元素所产生的的结果链接到一起而获得。通俗地讲就是:如果流中的元素经过mapper处理后得到的是流,那么将这些流拆开,把里面的元素提取出来,摊平成一个新的流。例如:
“H/e/l/l/o”经过splitString方法处理后得到的是流[“H”,“e”,“l”,“l”,“o”];那么oldStream中的元素全部经过splitString方法处理后得到是[[“H”,“e”,“l”,“l”,“o”],[“J”,“a”,“v”,“a”]],用flatMap摊平之后的流[“H”,“e”,“l”,“l”,“o”,“J”,“a”,“v”,“a”]
public class TestStream { public static void main(String[] args) throws IOException { Stream<String> oldStream = Stream.of("H/e/l/l/o", "j/a/v/a"); Stream<String> newStream = stream.flatMap(s -> splitString(s)); } private static Stream<String> splitString(String str){ String[] split = str.split("/"); Stream<String> stream = Arrays.stream(split); return stream; } }
3.4 limit–抽取
-
Stream limit(long maxSize)
产生一个流,其中包含了当前流中最初的maxSize个元素
public class TestStream { public static void main(String[] args) throws IOException { Stream<String> stream = Stream.of("h","e","l","l","o"); // 新的流中只含有三个元素:h e l Stream<String> newStream = stream.limit(3); // 产生5个随机数流 Stream<Double> stream = Stream.generate(Math::random).limit(5); } }
3.5 skip–跳过
-
Stream skip(long n)
产生一个流,它的元素是当前流中除了前n个元素之外的所有元素
public class TestStream { public static void main(String[] args) throws IOException { Stream<String> stream = Stream.of("h","e","l","l","o"); // 新的流中只含有两个元素:l o Stream<String> newStream = stream.skip(3); } }
3.6 distinct–去重
-
Stream<T>distinct()
将流中的重复元素剔除,保证流中的所有元素是不重复的
public class TestStream { public static void main(String[] args) throws IOException { Stream<String> stream = Stream.of("h","e","l","l","o"); // 新的流中元素为:h e l o,剔除了一个重复的l Stream<String> newStream = stream.distinct(); } }
3.7 sorted–排序
-
Stream<T> sorted()
-
Stream<T>sorted(Compartor<? super T> compartor)
产生一个新的流,它的元素是当前流中的所有与元素按照顺序排列的。第一个方法要求元素是实现了Comparable类的实例
public class TestStream { public static void main(String[] args) throws IOException { // 流中的元素排列顺序为:2 3 4 7 8 9 Stream<Integer> integerStream = Stream.of(3, 2, 9, 4, 7, 8).sorted(); // 流中的元素按照降序排列:9 8 7 4 3 2 Stream<Integer> integerStream2 = Stream.of(3, 2, 9, 4, 7, 8).sorted(Comparator.comparing(Integer::intValue).reversed()); } }
3.8 peek
-
Stream<T> peek(Consumer<? super T> action)
产生一个流,它与当前流中的相同,在获取其中每个元素时,会将器传递给action;这个方法可以来窥探流中的元素
public class TestStream { public static void main(String[] args) throws IOException { Object[] objects = Stream.of(1, 3, 5, 8, 9, 5) .filter(item -> item >= 5) .peek(System.out::print) // 它会将经过过滤后还剩的元素进行打印 .limit(9) .toArray(); System.out.println(objects.toString()); } }
4 Stream流的终结操作
流的终结操作将不会再返回流,而是将流中的元素处理成一个结果。
4.1 max,min–最大最小
-
Optional<T> max(Comparator<? super T> compartor)
-
Optional<T> min(Comparator<? super T> compartor)
分别获得这个流中的最大最小元素,如果流是空的,那么会产生一个空的Optional对象。
public class TestStream {
public static void main(String[] args) throws IOException {
Integer max = Stream.of(1, 3, 5, 2, 7, 80, 0).max(Comparator.comparing(Integer::intValue)).get();
System.out.println(max);// 输出 80
Integer min = Stream.of(-3, 3, 5, 2, 7, 80, 0).min(Comparator.comparing(Integer::intValue)).get();
System.out.println(min);// 输出-3
}
}
4.2 findFirst,findAny
-
Optional<T> findFirst()
-
OPtional<T> findAny()
分别产生这个流的第一个和任意一个元素,如果这个流为空,会产生一个空的Optional对象
public class TestStream {
public static void main(String[] args) throws IOException {
Integer first = Stream.of(1, 3, 5, 2, 7, 80, 0).filter(item -> item >= 5).findFirst().get();
System.out.println(first);// 输出5
Integer integer = Stream.of(9, 3, 5, 2, 7, 80, 0).findAny().get();
System.out.println(integer);// 输出任意一个元素
}
}
4.3 anyMatch,allMatch,noneMatch
-
boolean anyMatch(Predicate<? super T>predicate)
-
boolean allMatch(Predicate<? super T> predicate)
-
boolean noneMatch(Predicate<? super T> predicate)
用给定的断言条件去匹配这个流中的元素。匹配到任意一个元素返回true,所有元素都满足条件返回true,没有元素满足条件返回true
public class TestStream {
public static void main(String[] args) throws IOException {
boolean result = Stream.of(1, 3, 5, 2, 7, 80, 0).anyMatch(item -> item > 9);
System.out.println(result);// true
boolean result2 = Stream.of(1, 3, 5, 2, 7, 80, 0).allMatch(item -> item > 9);
System.out.println(result2);// false
boolean result3 = Stream.of(1, 3, 5, 2, 7, 80, 0).noneMatch(item -> item > 0);
System.out.println(result3);// false
}
}
4.4 foreach–遍历
-
void forEach(Consumer<? super T> action)
在流的每个元素上调用action。通常用来遍历流中的元素
public class TestStream {
public static void main(String[] args) throws IOException {
Stream.of(1, 3, 5, 2, 7, 80, 0).forEach(item -> System.out.println(item));
}
}
4.5 toArray
-
Object[] toArray()
-
<A> A[] toArray(IntFunction<A[]> generator)
产生一个对象数组,默认是Object[],如果想要具体的数组类型,列如String[],可以传入:String[]::new
public class TestStream {
public static void main(String[] args) throws IOException {
Object[] objects = Stream.of(1, 3, 5, 2, 7, 80, 0).toArray();
Integer[] integers = Stream.of(1, 3, 5, 2, 7, 80, 0).toArray(Integer[]::new);
}
}
4.6 reduce–约简
reduce约简,就是将流中的多个元素按照一定的规则简化成一个结果。
-
Optional<T>reduce(BinaryOperator<T> accumulator)
-
T reduce(T identity,BinaryOperator<T> accumulator)
-
U reduce (U identity,BiFunction<U,?superT,U)accumulator,BinaryOperator<U>combiner)
用给定的accumulator函数产生流中元素的累积总和。如果提供了幺元,那么第一个被累积的元素就是幺元。如果提供了组合器,那么他可以用来将分别累积的各个部分整合成总和
public class TestStream {
public static void main(String[] args) throws IOException {
// 将流中的元素累加
Optional<Integer> result = Stream.of(1, 2, 3, 4).reduce((x, y) -> x + y);
// 用初始值10去累加流中的元素
Integer result2 = Stream.of(1, 2, 3, 4).reduce(10, (x, y) -> x + y);
}
}
5 collect–收集结果
-
<R,A> R collect(Collector<? super T,A,R> collector)
使用给定的收集器来收集当前流中的元素,Collectors类有多种收集器的工厂方法。此方法比较常用
public class TestStream {
public static void main(String[] args) throws IOException {
List<String> list = getCityList().stream()
.map(city -> city.getCityName())
.collect(Collectors.toList());// 将城市名收集为一个list
list.iterator().forEachRemaining(item -> System.out.println(item));
}
public static List<City> getCityList(){
return new ArrayList<City>(){{
add(new City("成都","四川省",1800));
add(new City("泸州","四川省",600));
add(new City("广州","广东省",1800));
add(new City("深圳","广东省",1900));
add(new City("佛山","广东省",1000));
add(new City("西安","陕西省",1000));
}};
}
public static class City{
private String cityName;
private String province;
private Integer peopleCount;
...省略构造和getter,setter...
}
}
5.1 java.util.Steram.Collectors
-
static <T> Collector<T,?,List<T> toList()
-
static <T> Collector<T,?,Set<T> toSet()
产生一个将元素收集到列表或集合中的收集器
public class TestStream {
public static void main(String[] args) throws IOException {
Set<String> set = getCityList().stream()
.map(city -> city.getProvince())
.collect(Collectors.toSet());// 将省份收集为一个Set
set.iterator().forEachRemaining(item -> System.out.println(item));
}
}
-
static <T,C extends Collection> Collector<T,?,C> toCollection(Supplier<C>CollectionFactory)
产生一个将元素收集到任意集合中的收集器,可以传一个诸如TreeSet::new的构造器引用
public class TestStream {
public static void main(String[] args) throws IOException {
TreeSet<Integer> treeSet = getCityList().stream()
.map(city -> city.getPeopleCount())
.collect(Collectors.toCollection(TreeSet::new));// 收集为指定的类型TreeSet
treeSet.iterator().forEachRemaining(item -> System.out.println(item));
}
}
-
static Collector<CharSequence,?,String> joining()
-
static Collector<CharSequence,?,String> joining(CharSequence delimiter)
-
static Collector<CharSequence,?,String> joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix)
产生一个字符串的收集器。可以传入链接字符串的分隔符,还可以在第一个字符串之前增加前缀,在最后一个字符串增加后缀
public class TestStream {
public static void main(String[] args) throws IOException {
String cityName = getCityList().stream()
.map(city -> city.getCityName())
.collect(Collectors.joining());
System.out.println(cityName);// 成都泸州广州深圳佛山西安
String cityName2 = getCityList().stream()
.map(city -> city.getCityName())
.collect(Collectors.joining("-"));
System.out.println(cityName2);// 成都-泸州-广州-深圳-佛山-西安
String cityName3 = getCityList().stream()
.map(city -> city.getCityName())
.collect(Collectors.joining("-","城市名:","..."));
System.out.println(cityName3);// 城市名:成都-泸州-广州-深圳-佛山-西安...
}
}
-
static <T> Collector<T,?,IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper>
-
static <T> Collector<T,?,LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper>
-
static <T> Collector<T,?,DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper>
产生能够生成(Int|Long|Double)SummaryStatistics对象的收集器,通过它们可以获得将mapper应用于每个元素后产生的结果的数量,综合,平均值,最大值,最小值
public class TestStream {
public static void main(String[] args) throws IOException {
IntSummaryStatistics summaryInt = getCityList().stream()
.collect(Collectors.summarizingInt(City::getPeopleCount));
System.out.println(summaryInt.getMax());//1900
System.out.println(summaryInt.getAverage());//1350.0
System.out.println(summaryInt.getCount());//6
System.out.println(summaryInt.getMin());//600
System.out.println(summaryInt.getSum());//8100
}
}
-
static <T,K,U> Collector <T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper,Funciton<? super T,? extends U) valueMapper)
-
static <T,K,U> Collector <T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper,Funciton<? super T,? extends U) valueMapper,BinayOperator<U>mergeFunction)
-
static <T,K,U> Collector <T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper,Funciton<? super T,? extends U) valueMapper,BinayOperator<U>mergeFunction,Supplier<M>mapSupplier)
产生一个收集器,收集为一个Map,keyMapper函数用来从元素中获取键,valueMapper函数的返回作为值。默认情况下,当两个元素产生相同的键时,会抛出一个IllegalStateException异常。可以提供一个mergerFunction来合并具有相同键的值。
public class TestStream {
public static void main(String[] args) throws IOException {
// key是cityName;value是city
Map<String, City> map1 = getCityList().stream()
.collect(Collectors.toMap(City::getCityName, Function.identity()));
// key是cityNmae;value是peopleCount
Map<String, Integer> map2 = getCityList().stream()
.collect(Collectors.toMap(City::getCityName, City::getPeopleCount));
// key是province;value是peopleCount;因为省份有重复,会抛异常
Map<String, Integer> map3 = getCityList().stream()
.collect(Collectors.toMap(City::getProvince, City::getPeopleCount));
// key是province;value是peopleCount;当省份重复时,将它们的值累加
Map<String, Integer> map4 = getCityList().stream()
.collect(Collectors.toMap(City::getProvince, City::getPeopleCount, (existValue, newValue) -> existValue + newValue));
}
}
5.1.1 Collectors.groupingBy 群组和分区
-
static <T,K> Collector<T,?,Map<K,List<T>> groupingBy(Function<? super T,? extends K> classifer)
产生一个收集器,它会产生一个Map,键是将classifier应用于所有收集到的元素上所产生的结果,而值是具有相同的元素构成的一个个列表
public class TestStream {
public static void main(String[] args) throws IOException {
// 按照省份将city分组
Map<String, List<City>> map = getCityList().stream()
.collect(Collectors.groupingBy(City::getProvince));
}
}
-
static <T> Collector<T,?,Map<Boolean,List> partioningBy(predicate<? super T> predicate)
产生一个收集器,它会产生一个Map,键是true/false,值是满足/不满足的元素列表
public class TestStream {
public static void main(String[] args) throws IOException {
// 将城市按照人数是否超过1000万分组
Map<Boolean, List<City>> map2 = getCityList().stream()
.collect(Collectors.partitioningBy(city -> city.getPeopleCount() >= 1000));
System.out.println(map2);
}
}
5.1.2 下游收集器
因为Collectors.groupingBy 的结果是Map,Map的值是一个列表List<T>,这个List<T>中的元素还可以应用收集器,例如收集列表List<T>中元素的最大值,最小值,统计个数等等。
-
static <T,K,A,D>Collector<T,?,Map<K,D>> groupingBy(Function<? super T,? extends K> classfier,Collector<? super T,A,D) downStream)
产生一个收集器,收集结果是Map,Map的键是将classfier应用到所有收集到的元素上之后产生的结果,值是使用下游收集器具有相同的键的元素所产生的结果。通俗理解就是,stream流中的元素,先按classfier分组;得到结果Map<k,List<T>>,然后再将downStream应用到List<T>,产生的结果作为Map的值。
public class TestStream {
public static void main(String[] args) throws IOException {
// 将城市按省份分组,然后统计每个省份的城市数量
Map<String, Long> map = getCityList().stream()
.collect(Collectors.groupingBy(City::getProvince, Collectors.counting()));
System.out.println(map);// {陕西省=1, 广东省=3, 四川省=2}
// 将城市分组,然后取每个分组中人口数最大的城市
Map<String, Optional<City>> map2 = getCityList().stream()
.collect(Collectors.groupingBy(City::getProvince, Collectors.maxBy(Comparator.comparing(City::getPeopleCount))));
System.out.println(map2);
Map<String, Set<City>> map3 = getCityList().stream()
.collect(Collectors.groupingBy(City::getProvince, Collectors.toSet()));
System.out.println(map3);
// 将城市分组统计分组后城市人口数的平均值,最大,最小值等
Map<String, IntSummaryStatistics> map4 = getCityList().stream()
.collect(Collectors.groupingBy(City::getProvince, Collectors.summarizingInt(City::getPeopleCount)));
System.out.println(map4);
// 将城市按省份分组,然后取每个分组中的城市名,并将城市名用"-"拼接
Map<String, String> map5 = getCityList().stream()
.collect(Collectors.groupingBy(City::getProvince, Collectors.mapping(City::getCityName, Collectors.joining("-"))));
System.out.println(map5);// {陕西省=西安, 广东省=广州-深圳-佛山, 四川省=成都-泸州}
// 将城市按省份分组,然后统计每个组中的城市人口总和
Map<String, Integer> map6 = getCityList().stream()
.collect(Collectors.groupingBy(City::getProvince, Collectors.reducing(0, City::getPeopleCount, Integer::sum)));
System.out.println(map6);// {陕西省=1000, 广东省=4700, 四川省=2400}
}
}