Java8新特性——Stream流
一、Stream流简介
1.1 什么是Stream流?
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理
, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
———摘自菜鸟教程
流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
集合讲的是数据,流讲的是计算!
1.2 为什么使用Stream流?
Stream流可以对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。Stream API 借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性
。
1.3 Stream流的特性
- Stream 自己不会存储元素。
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
二、创建Stream流
一个数据源(如:集合、数组),获取一个流。
2.1 通过 Collection 扩展接口创建流
Collection 接口被扩展,提供了两个获取流的方法。
default Stream<E> stream() : 返回一个顺序流
default Stream<E> parallelStream() : 返回一个并行流
/**
* Collection 扩展接口
* 通过 Collection 的 Stream()方法或 parallelStream()方法创建 Stream
*/
System.out.println("======Collection======");
List<String> list = Arrays.asList("1", "2", "3", "zs", "abc");
Stream<String> stream1 = list.stream();
Stream<String> stream2 = list.parallelStream();
小贴士:
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理没歌数据块的流。
Java 8中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。
2.2 由数组创建流
Arrays 的静态方法 stream() 可以获取数组流。
static <T> Stream<T> stream(T[] array): 返回一个流
/**
* 由数组创建流
* 通过 Arrays中的静态方法 stream() 获取数组流
*/
System.out.println("======Arrays======");
IntStream intStream = Arrays.stream(new int[]{1, 2, 3});
LongStream longStream = Arrays.stream(new long[]{123L, 46L});
DoubleStream doubleStream = Arrays.stream(new double[]{12.3, 34.56});
Integer[] integerNums = new Integer[10];
Long[] longNums = new Long[10];
Double[] doubleNums = new Double[10];
Stream<Integer> integerStream2 = Arrays.stream(integerNums);
Stream<Long> longStream2 = Arrays.stream(longNums);
2.3 由值创建流
可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数。
public static<T> Stream<T> of(T... values) : 返回一个流
/**
* 由值创建流
* 通过 Stream()类中的 of()静态方法获取流
*/
System.out.println("======of()======");
Stream<String> streamValue = Stream.of("a", "b", "c");
2.4 由函数创建流
可以使用静态方法 Stream.iterate() 和Stream.generate(), 创建无限流。
迭代 public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
生成 public static<T> Stream<T> generate(Supplier<T> s)
System.out.println("======function()======");
//迭代(需要传入一个种子,也就是起始值,然后传入一个一元操作)
Stream<Integer> integerStream3 = Stream.iterate(0, x -> x + 2).limit(10);
integerStream3.forEach(System.out::println);
//生成(无限产生对象)
Stream<Double> doubleStream3 = Stream.generate(() -> Math.random());
doubleStream3.forEach(System.out::println);
三、Stream流的中间操作
一个中间操作链,对数据源的数据进行处理。
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
/**
* 中间操作
*/
List<UserEntity> userList = new ArrayList<>();
userList.add(new UserEntity("zhangsan", 20));
userList.add(new UserEntity("lisi", 28));
userList.add(new UserEntity("wangwu", 35));
userList.add(new UserEntity("xiaoming", 16));
userList.add(new UserEntity("Amay", 36));
userList.add(new UserEntity("Jack", 45));
3.1 筛选与切片
方法 | 描述 |
---|---|
filter(Predicate p) | 接收 Lambda , 从流中排除某些元素。 |
distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量。 |
skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
/**
* 筛选与切片
* filter:接受 Lambda,从流中排出某些元素;
* limit:截断流,使其元素不超过给定数量;
* skip(n):跳过元素,返回一个扔掉了前 n 个元素的流。如果流中元素不足 n个,就返回一个空流。(与 limit(n) 互补)
* distinct:筛选,通过流所生成元素的 hashCode()和equals()去除重复元素。
*/
// 1、filter
System.out.println("======filter======");
// 外部迭代(Java 8之前做的都是外部迭代)
System.out.println("======外部迭代======");
Iterator<UserEntity> iterator = userList.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
// 内部迭代:迭代操作在 Stream API 内部完成(对外部迭代进行的优化)
System.out.println("======内部迭代======");
System.out.println("测试中间操作...过滤所有年龄小于25的user对象");
Stream<UserEntity> stream = userList.stream()
// 所有的中间操作不会做任何处理
.filter(userEntity -> {
return userEntity.getAge() <= 25;
});
// 只有当做终止操作时,所有的中间操作会一次性的全部执行,这称为"惰性求值"
stream.forEach(System.out::println);
// 2、limit
System.out.println("======limit======");
System.out.println("测试中间操作...过滤后限定数量不超过3");
userList.stream().filter(userEntity -> userEntity.getAge() >= 30)
.limit(3).forEach(System.out::println);
// 3、skip
System.out.println("======skip======");
System.out.println("测试中间操作...过滤后跳过前2个元素");
userList.stream().filter(userEntity -> userEntity.getAge() >= 15)
.skip(2).forEach(System.out::println);
// 4、distinct
System.out.println("======distinct======");
System.out.println("测试中间操作...通过distinct进行筛选");
userList.stream().distinct().forEach(System.out::println);
3.2 映射
方法 | 描述 |
---|---|
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。 |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。 |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。 |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
/**
* 映射
*/
List<UserEntity> userList = Arrays.asList(new UserEntity("a",1),new UserEntity("ab",3),new UserEntity("c",11),new SUserEntityu("f",12));
Stream<UserEntity> stream = userList .stream();
// 去除list中所有的年龄
stream.map(UserEntity::getAge).forEach(System.out::println);
// 把所有年龄再返回一个集合
List<Integer> collect = stream.map(UserEntity::getAge).collect(Collectors.toList());
stream.flatMap(userEntity-> test1.filterCharacter(userEntity.getName())).forEach(System.out::println);
3.3 排序
方法 | 描述 |
---|---|
sorted() | 产生一个新流,其中按自然顺序排序 |
sorted(Comparator comp) | 产生一个新流,其中按比较器顺序排序 |
/**
* stream排序sorted
*/
// 1、匿名内部类的方式
Stream<UserEntity> sortedStream1 = userEntities.stream();
sortedStream1.sorted(new Comparator<UserEntity>() {
@Override
public int compare(UserEntity o1, UserEntity o2) {
// 升序
return o1.getAge() - o2.getAge();
}
}).forEach(new Consumer<UserEntity>() {
@Override
public void accept(UserEntity userEntity) {
System.out.println("使用匿名内部类的方式排序:" + userEntity.toString());
}
});
// 2、lambda的方式
Stream<UserEntity> sortedStream2 = userEntities.stream();
sortedStream2.sorted((o1, o2) -> o1.getAge() - o2.getAge())
.forEach(userEntity -> System.out.println("使用lambda的方式排序:" + userEntity.toString()));
四、Stream流的终止操作
一个终止操作,执行中间操作链,并产生结果。
/**
* 终止操作
*/
List<UserEntity> userList = new ArrayList<>();
userList.add(new UserEntity("zhangsan", 20));
userList.add(new UserEntity("lisi", 28));
userList.add(new UserEntity("wangwu", 35));
userList.add(new UserEntity("xiaoming", 16));
userList.add(new UserEntity("Amay", 36));
userList.add(new UserEntity("Jack", 45));
4.1 查找与匹配
方法 | 描述 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了) |
/**
* streamMatch匹配--noneMatch
* noneMatch表示判断条件里的元素,所有的都不是,就返回true
*/
System.out.println("======noneMatch======");
// 1、匿名内部类的方式
Stream<UserEntity> noneMatch1 = userList.stream();
boolean result = noneMatch1.noneMatch(new Predicate<UserEntity>() {
@Override
public boolean test(UserEntity userEntity) {
return userEntity.getAge() > 32;
}
});
System.out.println("noneMatch使用匿名内部类的方式:" + result);
// 2、lambda的方式
Stream<UserEntity> noneMatch2 = userList.stream();
boolean result2 = noneMatch2.noneMatch((user) -> user.getAge() > 35);
System.out.println("noneMatch使用lambda的方式:" + result2);
/**
* streamMatch匹配--allMatch
* allMatch表示判断条件里的元素,所有都是就返回true
*/
System.out.println("======allMatch======");
// 1、匿名内部类的方式
Stream<UserEntity> allMatch1 = userList.stream();
boolean result3 = allMatch1.allMatch(new Predicate<UserEntity>() {
@Override
public boolean test(UserEntity userEntity) {
return userEntity.getAge() < 54;
}
});
System.out.println("allMatch使用匿名内部类的方式:" + result3);
// 2、lambda的方式
Stream<UserEntity> allMatch2 = userList.stream();
boolean result4 = allMatch2.allMatch((user) -> user.getAge() <54);
System.out.println("allMatch使用lambda的方式:" + result4);
/**
* streamMatch匹配--anyMatch
*/
System.out.println("======anyMatch======");
// 1、匿名内部类的方式
Stream<UserEntity> anyMatch1 = userList.stream();
boolean result5 = anyMatch1.anyMatch(new Predicate<UserEntity>() {
@Override
public boolean test(UserEntity userEntity) {
return userEntity.getAge() > 20;
}
});
System.out.println("anyMatch使用匿名内部类的方式:" + result5);
// 2、lambda的方式
Stream<UserEntity> anyMatch2 = userList.stream();
boolean result6 = anyMatch2.anyMatch((user) -> user.getAge() > 20);
System.out.println("anyMatch使用lambda的方式:" + result6);
/**
* findFirst
*/
System.out.println("======findFirst======");
Optional<UserEntity> findFirst = userList.stream().sorted(((o1, o2) -> Double.compare(o1.getAge(), o2.getAge()))).findFirst();
System.out.println("findFirst:" + findFirst.get());
/**
* findAny
*/
System.out.println("======findAny======");
Optional<UserEntity> findAny = userList.stream().filter(userEntity -> userEntity.getName().equalsIgnoreCase("zhangsan")).findAny();
System.out.println("findAny:" + findAny.get());
/**
* max
*/
System.out.println("======max======");
Optional<Integer> max = userList.stream().map(UserEntity::getAge).max(Double::compare);
System.out.println("max:" + max);
/**
* min
*/
System.out.println("======min======");
Optional<Integer> min = userList.stream().map(UserEntity::getAge).min(Double::compare);
System.out.println("min:" + min);
注意:流进行了终止操作后不能再次使用!
4.2 归约
方法 | 描述 |
---|---|
reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。 返回 T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 Optional< T > |
/**
* stream将reduce求和--基本数据类型
*/
System.out.println("======reduce——基本数据类型======");
// 1、使用匿名内部类的方式
Stream<Integer> integerStream1 = Stream.of(10, 30, 80, 60, 10, 70);
Optional<Integer> reduce = integerStream1.reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
});
System.out.println(reduce.get());
// 2、使用lambda的方式
Stream<Integer> integerStream2 = Stream.of(10, 30, 80, 60, 10, 70);
Optional<Integer> reduce2 = integerStream2.reduce((a1, a2) -> a1 + a2);
System.out.println(reduce2.get());
/**
* stream将reduce求和--对象
*/
System.out.println("======reduce——对象======");
// 1、使用匿名内部类的方式
// 1、使用匿名内部类的方式
Stream<UserEntity> objStream1 = userEntities.stream();
Optional<UserEntity> reduce3 = objStream1.reduce(new BinaryOperator<UserEntity>() {
@Override
public UserEntity apply(UserEntity userEntity, UserEntity userEntity2) {
userEntity.setAge(userEntity.getAge() + userEntity2.getAge());
return userEntity;
}
});
System.out.println(reduce3.get().getAge());
// 2、使用lambda的方式
Stream<UserEntity> objStream2 = userEntities.stream();
Optional<UserEntity> reduce4 = objStream2.reduce((user1, user2) -> {
user1.setAge(user1.getAge() + user2.getAge());
return user1;
});
System.out.println(reduce4.get().getAge());
4.3 收集
方法 | 描述 |
---|---|
collect(Collector c) | 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法 |
Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下:
方法 | 返回类型 | 描述 |
---|---|---|
toList | List< T > | 把流中元素收集到List |
toSet | Set< T > | 把流中元素收集到Set |
toCollection | Collection< T > | 把流中元素收集到创建的集合 |
counting | Long | 计算流中元素的个数 |
summingInt | Integer | 对流中元素的整数属性求和 |
averagingInt | Double | 计算流中元素Integer属性的平均值 |
summarizingInt | IntSummaryStatistics | 收集流中Integer属性的统计值。 如:平均值 |
joining | String | 连接流中每个字符串 |
maxBy | Optional< T > | 根据比较器选择最大值 |
minBy | Optional< T > | 根据比较器选择最小值 |
reducing | 归约产生的类型 | 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值 |
collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器,对其结果转换函数 |
groupingBy | Map<K, List< T >> | 根据某属性值对流分组,属性为K,结果为V |
partitioningBy | Map<Boolean, List< T >> | 根据true或false进行分区 |
/**
* collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法。
*/
// toList():把流中元素收集到 List
System.out.println("======toList()======");
List<String> collectList = userList.stream().map(UserEntity::getName).collect(Collectors.toList());
collectList.forEach(System.out::println);
// toSet():把流中元素收集到 Set
System.out.println("======toSet()======");
Set<String> collectSet = userList.stream().map(UserEntity::getName).collect(Collectors.toSet());
collectSet.forEach(System.out::println);
// toCollection():把流中元素收集到创建的集合
System.out.println("======toCllection()======");
HashSet<String> collectHashSet = userList.stream().map(UserEntity::getName).collect(Collectors.toCollection(HashSet::new));
collectHashSet.forEach(System.out::println);
// counting():计算流中元素的个数
System.out.println("======counting()======");
Long collectLong = userList.stream().collect(Collectors.counting());
System.out.println("流中元素的个数:" + collectLong);
// summingInt():对流中元素的整数属性求和
System.out.println("======summingInt()======");
Integer summingInt = userList.stream().collect(Collectors.summingInt(UserEntity::getAge));
System.out.println("对流中的整数属性求和:" + summingInt);
// summarizingInt():收集流中Integer属性的统计值。 如:平均值
System.out.println("======summarizingInt()======");
IntSummaryStatistics intSummaryStatistics = userList.stream().collect(Collectors.summarizingInt(UserEntity::getAge));
System.out.println("平均值:" + intSummaryStatistics.getAverage());
System.out.println("最大值:" + intSummaryStatistics.getMax());
// joining():连接流中每个字符串("以什么符号分割", "以什么符号开头", "以什么符号结尾")
System.out.println("======joining()======");
String joiningStr = userList.stream().map(UserEntity::getName).collect(Collectors.joining(",", "---", "---"));
System.out.println(joiningStr);
// maxBy():根据比较器选择最大值
System.out.println("======maxBy()======");
Optional<Integer> collectMaxBy = userList.stream().map(UserEntity::getAge).collect(Collectors.maxBy(Integer::compare));
System.out.println(collectMaxBy.get());
// minBy():根据比较器选择最小值
System.out.println("======minBy()======");
Optional<UserEntity> collectMinBy = userList.stream().collect(Collectors.minBy(((o1, o2) -> Integer.compare(o1.getAge(), o2.getAge()))));
System.out.println(collectMinBy.get());
// reducing():从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
System.out.println("======reducing()======");
Optional<Integer> collectReducing = userList.stream().map(UserEntity::getAge).collect(Collectors.reducing(Integer::sum));
System.out.println(collectReducing);
// groupingBy():根据某属性值对流分组,属性为K,结果为V
System.out.println("======groupingBy()======");
Map<Integer, List<UserEntity>> listMap = userList.stream().collect(Collectors.groupingBy(UserEntity::getAge));
System.out.println("根据年龄对流分组:" + listMap);
// 多级分组
Map<Integer, Map<String, List<UserEntity>>> groupMap = userList.stream().collect(Collectors.groupingBy(UserEntity::getAge, Collectors.groupingBy(u -> {
if (u.getAge() >= 60)
return "老年";
else if (u.getAge() >= 35)
return "中年";
else
return "成年";
})));
System.out.println(groupMap);
// partitioningBy():根据true或false进行分区
System.out.println("======partitioningBy()======");
Map<Boolean, List<UserEntity>> partitioningMap = userList.stream().collect(Collectors.partitioningBy(u -> u.getAge() >= 500));
System.out.println(partitioningMap);