Java 8中添加了一个新的功能称为Stream流,可以通过使用一种声明的方式处理数据。Stream 流使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream这种风格将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
如果把集合作为流的数据源,创建流时不会导致数据流动; 如果流的终止操作需要值时,流就会从集合中获取值; 流只使用一次。流的中心思想就是延迟计算,流直到需要时才计算值。
Stream可以由数组或集合创建,对流的操作分为两种:
可以创建普通的Stream流和特定的Stream流。
/**
* stream的分类
*/
// 普通流
List<String> list1 = Arrays.asList("1", "2", "3");
Stream<String> stream1 = list1.stream();
List<Integer> list2 = Arrays.asList(1, 2, 3);
Stream<Integer> stream2 = list2.stream();
// 特化流
IntStream stream3 = IntStream.of(1, 2, 3);
stream3.forEach(System.out::println);
也可以创建stream和 parallelStream,stream是顺序流,由主线程按顺序对流执行操作,而 parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。并且只有当数据量比较大时,我们使用并行流才具备优势。
// 串行
Stream<String> stream4 = Stream.of("1", "2", "3");
// 并行
// 1.
Stream<String> parallelStream1 = list1.parallelStream();
parallelStream1.forEach(System.out::print);
// 2.
Stream<String> parallelStream2 = list1.stream().parallel();
通过使用 java.util.Collection.stream() 方法用集合创建流,使用 java.util.Arrays.stream(T[]array)方法用数组创建流。
List<String> list = Arrays.asList("1", "2", "3");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
int[] array={1, 2, 3};
IntStream stream = Arrays.stream(array);
通过使用 Stream的静态方法:of()、iterate()、generate()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
Stream<Integer> stream2 = Stream.iterate((int)Math.random(), (x) -> x + 1).limit(4);
stream2.forEach(System.out::println);
Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);
通过使用Stream的builder()方法创建构建器,通过构建器创建stream流。
Stream.Builder<String> builder = Stream.builder();
builder.add("1");
builder.add("2");
builder.add("3");
Stream<String> stream4 = builder.build();
stream4.forEach(System.out::print);
使用stream流之前,我们先来理解一个概念:Optional类。
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。因此,Optional 类的引入很好的解决空指针异常。
ofNullable方法
//为指定的值创建Optional对象,不管所传入的值为null不为null,创建的时候都不会报错
Optional<String> Optional1 = Optional.ofNullable(null);
Optional<String> Optional2 = Optional.ofNullable("picacho");
ifPresent方法
String str = "picacho";
Optional<String> optional = Optional.ofNullable(str);
// 如果值不是为空的,那么就可以直接在这个里面来进行处理
optional.ifPresent(s -> System.out.println(str+"hello"));
orElse方法
Optional<Object> empty = Optional.empty();
System.out.println(empty.orElse("hello"));
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
// 遍历输出符合条件的元素
list.stream().filter(x -> x > 3).forEach(System.out::println);
// 匹配第一个
Optional<Integer> findFirst = list.stream().filter(x -> x > 3).findFirst();
// 匹配任意(适用于并行流)
Optional<Integer> findAny = list.parallelStream().filter(x -> x > 3).findAny();
// 是否包含符合特定条件的元素
boolean anyMatch = list.stream().anyMatch(x -> x < 2);
System.out.println("匹配第一个值:" + findFirst.get());
System.out.println("匹配任意一个值:" + findAny.get());
System.out.println("是否存在大于6的值:" + anyMatch);
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("zhangsan", 8, 3000));
personList.add(new Person("lisi", 18, 5000));
personList.add(new Person("wangwu", 28, 7000));
personList.add(new Person("sunliu", 38, 9000));
// 筛选出年龄大于18
List<Person> personList1 = personList.stream().filter(x -> x.getAge() > 18).collect(Collectors.toList());
personList1.forEach(System.out::println);
List<String> list = Arrays.asList("123", "1234", "12345", "12");
Comparator<? super String> comparator = Comparator.comparing(String::length);
Optional<String> max = list.stream().max(comparator);
System.out.println(max.get());
Optional<String> min = list.stream().min(comparator);
System.out.println(min.get());
long count = list.stream().filter(x -> x.length() > 3).count();
System.out.println(count);
map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
// 将姓名大写
List<String> list1 = personList.stream().map(person -> person.getName().toUpperCase())
.collect(Collectors.toList());
System.out.println(list1);
// 工资涨1000
List<Person> list2 = personList.stream().map(person -> {
person.setAccount(person.getAccount() + 1000);
return person;
}).collect(Collectors.toList());
System.out.println(list2);
// 将两个字符数组合并称一个新的字符数组
String[] arr = {"z, h, a, n, g", "s, a, n"};
List<String> collect = Arrays.stream(arr).flatMap(x -> {
String[] array = x.split(",");
Stream<String> streamTmp = Arrays.stream(array);
return streamTmp;
}).collect(Collectors.toList());
System.out.println(collect);
Collectors提供了一系列用于数据统计的静态方法:
计数: count
平均值: averagingInt、 averagingLong、 averagingDouble
最值: maxBy、 minBy
求和: summingInt、 summingLong、 summingDouble
//统计员工人数
Long count1 = personList.stream().collect(Collectors.counting());
//求平均工资
Double average = personList.stream().collect(Collectors.averagingDouble(Person::getAccount));
//求最高工资
Optional<Integer> max1 = personList.stream().map(Person::getAccount).collect(Collectors.maxBy(Integer::compare));
//求工资之和
Integer sum = personList.stream().collect(Collectors.summingInt(Person::getAccount));
//一次性统计所有信息
DoubleSummaryStatistics collect1 = personList.stream().collect(Collectors.summarizingDouble(Person::getAccount));
System.out.println("统计员工人数:"+count);
System.out.println("求平均工资:"+average);
System.out.println("求最高工资:"+max);
System.out.println("求工资之和:"+sum);
System.out.println("一次性统计所有信息:"+collect1);
// 取出大于18岁的员工转为map
Map<String, Person> collect3 = personList.stream().filter(x -> x.getAge() > 18).collect(Collectors.toMap(Person::getName, y -> y));
System.out.println(collect3);
归约,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
List<Integer> list3 = Arrays.asList(1, 2, 3, 4);
//求和
Optional<Integer> reduce = list3.stream().reduce((x,y) -> x + y);
System.out.println("求和:"+reduce.get());
//求最大值
Optional<Integer> reduce3 = list3.stream().reduce((x,y) -> x > y ? x : y);
System.out.println("求最大值:"+reduce3.get());
joining可以将stream中的元素用特定的连接符(默认直接连接)连接成一个字符串。
String names = personList.stream().map(person -> person.getName()).collect(Collectors.joining("-"));
System.out.println(names);
分区:将stream按条件分为两个 Map。
分组:将集合分为多个Map。
List<Person> personList3 = new ArrayList<Person>();
personList3.add(new Person("zhangsan", 18, 3000));
personList3.add(new Person("lisi", 18, 5000));
personList3.add(new Person("wangwu", 28, 7000));
personList3.add(new Person("sunliu", 38, 9000));
// 将员工按薪资是否高于8000分组
Map<Boolean, List<Person>> part = personList3.stream().collect(Collectors.partitioningBy(x -> x.getAccount() > 8000));
// 将员工先按年龄分组,再按工资分组
Map<Integer, Map<String, List<Person>>> group = personList3.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.groupingBy(Person::getName)));
System.out.println("员工按薪资是否大于8000分组情况:" + part);
System.out.println("员工按性别、地区:" + group);
// 按工资升序排序(自然排序)
List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getAccount)).map(Person::getName)
.collect(Collectors.toList());
// 按工资倒序排序
List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getAccount).reversed())
.map(Person::getName).collect(Collectors.toList());
// 先按工资再按年龄升序排序
List<String> newList3 = personList.stream()
.sorted(Comparator.comparing(Person::getAccount).thenComparing(Person::getAge)).map(Person::getName)
.collect(Collectors.toList());
// 先按工资再按年龄自定义排序(降序)
List<String> newList4 = personList.stream().sorted((p1, p2) -> {
if (p1.getAccount() == p2.getAccount()) {
return p2.getAge() - p1.getAge();
} else {
return p2.getAccount() - p1.getAccount();
}
}).map(Person::getName).collect(Collectors.toList());
System.out.println("按工资升序排序:" + newList);
System.out.println("按工资降序排序:" + newList2);
System.out.println("先按工资再按年龄升序排序:" + newList3);
System.out.println("先按工资再按年龄自定义降序排序:" + newList4);
String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "d", "e", "f", "g" };
Stream<String> stream11 = Stream.of(arr1);
Stream<String> stream12 = Stream.of(arr2);
// concat:合并两个流 distinct:去重
List<String> newList11 = Stream.concat(stream11, stream12).distinct().collect(Collectors.toList());
// limit:限制从流中获得前n个数据
List<Integer> collect11 = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
// skip:跳过前n个数据
List<Integer> collect12 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
System.out.println("流合并:" + newList11);
System.out.println("limit:" + collect11);
System.out.println("skip:" + collect12);
Sream流还有很多更高级的用法,需要多看API,多练习来掌握该类的使用。在项目中使用该类可以使得代码更加精炼和美观。