学习流之前建议先学习lambda表达式的应用。
1.流简介
Stream的优点:声明性,可复合,可并行。这三个特性使得stream操作更简洁,更灵活,更高效。
Stream的操作有两个特点:可以多个操作链接起来运行;内部迭代;
集合和流的区别:集合包含目前的所有元素,你要什么就去集合里取。流是你要什么,然后去计算得到你需要的值。流是按照需求生成的。
流只能遍历一次,用完之后就不存在了,只能重新创建流才能使用。例如:
IntStream i = IntStream.of(1,2,3,4); i.forEach(System.out::println);<!--forEach输入Consumer--> i.forEach(System.out::println);<!--报错,stream has already been operated upon or closed-->
Collection接口使用的迭代,比如for-each,叫做外部迭代。流则是内部迭代,你只要告诉它干什么就行了。外部迭代你可以看到每一次迭代,而内部迭代你看不到,流帮你做了。
2.流操作
java.util.stream.Stream有许多操作,但是可以分为两类:一种为中间操作,一种为终端操作。
中间操作就是操作后返回一个流,比如map,filter,limit等等,中间操作只有在终端操作时才执行。
终端操作就是把流变为结果不是流的值,比如void,List,Integer等等。比如forEach,count,collect等等。
使用流一般包含三个动作:生成流,中间操作,终端操作。
3.使用流
3.1构建流
由值生成流:
IntStream i = IntStream.of(1,2,3,4); Stream<String> s = Stream.of("a","b","c"); Stream<String> emptyStream = Stream.empty();<!--创建一个空的流-->
由数组创建流:
int[] numbers = {1,2,3,4}; IntStream intStream = Arrays.stream(numbers);
由文件生成流:
java.nio.file.Files很多方法都会返回流。比如Files.lines会返回指定文件各行构成的字符串流
// hello // world // stream >>>data.txt数据 try(Stream<String> lines = Files.lines(Paths.get("D:\\Desktop\\data.txt"))){ long words =lines.flatMap(line->Arrays.stream(line.split(""))).distinct().count(); System.out.println(words); }catch(IOException e){ e.printStackTrace(); }
由函数生成流:
Stream Api有两个静态方法生成流:Stream.iterate,Stream.generate。这两个方法可以生成无限流,可以无限制的计算产生流数据。一定要加limit限制大小。
Stream.iterate(0, x->x+1).limit(10).forEach(System.out::println); Stream.generate(()->2).limit(10).forEach(System.out::println);
一般使用的是集合生成的流,Collection接口有默认方法产生流。只要实现Collection的类都可以用这个方法。
List<Integer> list = Arrays.asList(1,2,3,4); list.stream().forEach(System.out::println);
3.2筛选
流的filter方法,参数为Predicate。返回满足Predicate的元素。也就是返回true的元素。
List<Integer> list = Arrays.asList(1,2,3,4); list.stream().filter(i->i>=2).forEach(System.out::println);
流的distinct方法返回各异的元素,返回的元素不能重复。
List<Integer> list = Arrays.asList(1,2,3,4,2,3); list.stream().distinct().forEach(System.out::println);
流的limit方法限制最多返回多少元素。
List<Integer> list = Arrays.asList(1,2,3,4,2,3); list.stream().limit(2).forEach(System.out::println);
流的skip方法不要前多n个元素,n大于满足条件的元素个数就返回空的流。
List<Integer> list = Arrays.asList(1,2,3,4,2,3); list.stream().filter(i->i>=2).skip(2).forEach(System.out::println);
3.3映射
流的map方法作用于每个元素,并且返回一个新的元素,参数为Function。
List<Integer> list = Arrays.asList(1,2,3,4,2,3); list.stream().filter(i->i>=2).map(a->"map"+a).forEach(System.out::println);
map可以改变Stream的类型,上面就是把Stream<Integer>变为Stream<String>了。
流的flatMap方法可以把各个数组映射的流合为一个流。例如得到文件字母的个数。
lines.flatMap(line->Arrays.stream(line.split(""))).distinct().count();
如果直接用map(line->line.spilt(“”))得到的是很多个Stream<String[]>。
如果用map(line->Arrays.stream(line.split(“”)))得到的是很多个Stream<String>。
flatMap就是把很多个Stream<String>合为一个Stream<String>这样就可以算字母个数了。不然分为很多个流不能算字母个数。
3.4查找与匹配
anyMatch方法参数为Predicate。返回一个boolean值,表示流中是否有元素满足Predicate。
List<Integer> list = Arrays.asList(1,2,3,4,2,3); boolean result = list.stream().anyMatch(i->i==2);
allMatch方法与anyMatch差不多,表示所有的元素都满足才返回true。
noneMatch方法表示没有元素满足。
这三个操作都是短路操作。短路操作就是不需要遍历所有的元素就能得到结果。就可&&和||操作符一样。limit方法就是一个短路操作。
findAny方法,没有参数,表示返回随机的一个元素。返回一个Optional,因为有可能元素不存在。
List<Integer> list = Arrays.asList(1,2,3,4,2,3); Optional<Integer> result = list.stream().findAny(); System.out.println(result.orElse(0));
findFirst方法,没有参数,返回第一个元素。返回Optional。
3.5归约
流的reduce操作就是将流中的元素结合得到一个值。根据reduce方法中的操作,两两结合得到一个值,然后跟下一个元素结合,就这样一直到最后一个元素,得到最终的值。
List<Integer> list = Arrays.asList(1,2,3,4,2,3); Optional<Integer> sum = list.stream().reduce(Integer::sum);<!--reduce可以指定初始值--> Optional<Integer> max = list.stream().reduce(Integer::max); Optional<Integer> min = list.stream().reduce(Integer::min);
流还有max和min方法,参数为Comparator。
List<Integer> list = Arrays.asList(1,2,3,4,2,3);
Optional<Integer> max = list.stream().max(comparing(Integer::intValue));
Optional<Integer> min = list.stream().min(comparing(Integer::intValue));
3.6数值流
上面讲的操作如Stream<Integer>会有装箱拆箱的操作。Stream中有IntStream、 DoubleStream和
LongStream分别表示流中的元素为int,double,long,这样可以避免装箱拆箱。
mapToInt、 mapToDouble和mapToLong着三个方法可以将Stream<T>转为数值流。数值流中有max,min,sum,average等方法。
List<A> list = Arrays.asList(new A(1),new A(5),new A(3));
int sum = list.stream().mapToInt(a->a.getNum()).sum();
OptionalInt max = list.stream().mapToInt(a->a.getNum()).max();
OptionalInt min = list.stream().mapToInt(a->a.getNum()).min();
OptionalDouble ave = list.stream().mapToInt(a->a.getNum()).average();
boxed() 方法可以将数值流转为对象流。
List<A> list = Arrays.asList(new A(1),new A(5),new A(3));
Stream<Integer> i = list.stream().mapToInt(a->a.getNum()).boxed();
IntStream
和
LongStream
的静态方法,帮助生成这种范围:
range
和
rangeClosed
。这两个方法都是第一个参数接受起始值,第二个参数接受结束值。但
range
是不包含结束值的,而
rangeClosed
则包含结束值。
IntStream.range(1, 100).filter(i->i%2==0).count();