理解java中的流Stream
概念:
流:代表任何有能力产出数据的数据源对象或者是有能力接受数据的接收端对象<Thinking in Java>,换句话说:是对输入或输出设备(文件,网络,内存)的抽象。
流的本质: 数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
流的创建:
//集合
list.stream();
list.parallelStream();
//数组
Arrays.stream(array);//array是数组
//数字
IntStream stream1;
LongStream stream2;
//无限流
Stream.generate(Supplier s);//返回无限顺序无序流,其中每个元素由提供的供应商生成。这适用于生成恒定流,随机元素流等。如:Stream.generate(new Random()::nextInt)
IntStream intStream = random.ints(0, 100);//创建一个无穷大的int类型的数字流,这些数字在0(包括0)和100(不包括100)之间
DoubleStream doubleStream = random.doubles();//无穷大的double类型的数字流
流中间操作:
无状态操作:
map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); // 获取对应的平方数 List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
filter 过滤器
//过滤出空字符串: List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量 long count = strings.stream().filter(string -> string.isEmpty()).count();
peek 中间操作,类似foreach
Random random = new Random(); random.ints().limit(10).peek(System.out::println); //输出10个数字
foreach 中间操作
Random random = new Random(); random.ints().limit(10).forEach(System.out::println); //输出10个数字
unordered产生一个与当前流中元素相同的无序流, 当流本身就是无序或流已经无序, 会返回流本身
Stream.of(5, 1, 2, 6, 3, 7, 4).forEach(System.out::println); //
有状态操作:
sorted 排序
Random random = new Random(); random.ints().limit(10).sorted().forEach(System.out::println); //打印10条数据,并排序
distinct 去重
Random random = new Random(); random.ints().limit(10).distinct().forEach(System.out::println); //打印10条数据,去重重复的数据条
limit/skip 限制/跳过
Random random = new Random(); random.ints().limit(10).skip(5).forEach(System.out::println); //打印后5条数据
流终止操作:
短路操作:
findFirst/findAny:
在串行的流中,findAny和findFirst返回的,都是第一个对象;而在并行的流中,findAny返回的是最快处理完的那个线程的数据,所以说,在并行操作中,对数据没有顺序上的要求,那么findAny的效率会比findFirst要快的
List<String> list = new ArrayList(); list.add("a"); list.add("b"); Optional<String> aa = list.stream().filter(str -> !str.equals("a")).findFirst(); System.out.println(aa.get()); //输出b
boolean anyMatch(Predicate<? super T> predicate)只要有一个条件满足即返回true
allMatch 必须全部都满足才会返回true
noneMatch 全都不满足才会返回true
List<String> list = new ArrayList(); list.add("a");list.add("b");list.add("c"); boolean f = list.stream().anyMatch(str->!str.equals("a")); //返回true
非短路操作:
forEach/forEachOrderd
Stream.of("AAA","BBB","CCC").parallel().forEach(s->System.out.println("Output:"+s)); //输出顺序不确定,因为是并行处理 Stream.of("AAA","BBB","CCC").parallel().forEachOrdered(s->System.out.println("Output:"+s)); //按顺序输出AAA BBB CCC
collect/toArray 转换成集合或者数组
List a = Stream.of("AAA","BBB","CCC").collect(Collectors.toList());
Object[] b = Stream.of("AAA","BBB","CCC").toArray();
reduce 根据指定的计算模型将Stream中的值计算得到一个最终结果
方法一:
Optional<T> reduce(BinaryOperator<T> accumulator);
- 对
Stream
中的数据通过累加器accumulator
迭代计算,最终得到一个Optional
对
Optional r = Stream.of(1,2,3).reduce( (a,b)->{a+=b;return a;}); //r.get()=6
方法二:
T reduce(T identity, BinaryOperator<T> accumulator);
- 提供一个跟Stream中数据同类型的初始值identity,通过累加器accumulator迭代计算Stream中的数据,得到一个跟Stream中数据相同类型的最终结果
int r = Stream.of(1,2,3).reduce(100,(a,b)->{a+=b;return a;}); // r=106
方法三:
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
- 提供一个不同于
Stream
中数据类型的初始值,通过累加器规则迭代计算Stream
中的数据,最终得到一个同初始值同类型的结果
ArrayList<Integer> r = Stream.of(1,2,3).reduce( newList,(a,b)->{a.add(b);return a;},(acc, item) -> null); System.out.println(r); //[1, 2, 3]
min/max/count
int r1 = Stream.of(1,2,3).max(Integer::compare).get();//3 int r2 = Stream.of(1,2,3).min(Integer::compare).get();//1 Long a = Stream.of(2,1,4,5,3).count(); //5
并行流:
parallelStream提供了流的并行处理,它是Stream的另一重要特性,其底层使用Fork/Join框架实现。简单理解就是多线程异步任务的一种实现。
适合没有线程安全问题、较单纯的数据处理任务。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); numbers.parallelStream().forEach(num->System.out.println(num)); //输出: 6 5 7 8 3 9 4 2 1 (随机的)
分组:
Map<String, List<Person>> collect = list.stream().collect(Collectors.groupingBy(person -> person.getName()));//根据姓名分组