Java8中有两大最为重要的改变。第一个是Lambda 表达式;另外一个则是Stream API(java.util.stream.*)。
Stream 是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
流(Stream) 到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”
注意:
- 无存储。stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等
- 为函数式编程而生。对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。
- 惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
- 可消费性。stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成
Java8 中的Collection 接口被扩展,提供了两个获取流的方法:
/**
*返回一个顺序流
*/
default Stream<E> stream() :
/**
*返回一个并行流
*/
default Stream<E> parallelStream() :
Java8 中的Arrays 的静态方法stream() 可以获取数组流:
/**
*返回一个流
*/
static <T> Stream<T> stream(T[] array):
maps不支持流。然而现在maps包括了许多新的非常有用的方法用于执行通用任务,这部分内容在一篇博客中进行讲解。
创建流
// 1、数组
String[] arr = new String[]{"大仙", "半仙", "朱大仙"};
Stream<String> arrStream = Arrays.stream(arr);
// 2、集合
List<String> list = Arrays.asList("大仙", "半仙", "朱大仙");
Stream<String> colStream = list.stream();
// 3、值
Stream<String> stream = Stream.of("大仙", "半仙", "朱大仙");
下面我们准备一些数据进行说明,前期准备如下内容:
public class Test {
/**
* 人的实体类
* @author 大仙
*
*/
@Data
static class People{
String name;
Integer age;
public People(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
}
List<People> list = Arrays.asList(
new People("大仙",2),
new People("朱半仙",15),
new People("半仙",8),
new People("大神",10),
new People("超神",9));
public static void main(String[] args) {
}
}
1、findFirst() :使用该方法获取第一个元素
/**
* findFirst 获取第一个元素.
*/
public void FindFirstEle(){
People people = list.stream().findFirst().get();
System.out.println(people);
}
2、forEach() 方法
/**
* forEach() 迭代输出每条数据.
*/
public void forEachEle() {
// java 8 前
System.out.println("java 8 前");
for(People people: list){
System.out.println(people);
}
// java 8 lambda
System.out.println("java 8 lambda");
list.forEach(people -> System.out.println(people));
// java 8 stream lambda
System.out.println("java 8 stream lambda");
list.stream().forEach(people -> System.out.println(people));
}
3、sorted() 方法:代码注释里面有详细的说明,这里就不重复赘述了,注意一点,就是stream的操作不影响原集合。
/**
* sort 排序.
*/
public void sortEle() {
System.out.println("-----排序前-----");
list.forEach(people -> System.out.println(people));
System.out.println("-----排序后-----");
// java 8 前
// System.out.println("java 8 前");
// Collections.sort(list, new Comparator<People>() {
// @Override
// public int compare(People o1, People o2) {
// return o1.getAge().compareTo(o2.getAge());
// }
// });
// for (People people : list) {
// System.out.println(people);
// }
// java 8 stream 方法引用
System.out.println("java 8 stream 方法引用");
//Java 8 中我们可以通过 "::" 关键字来访问类的构造方法,对象方法,静态方法。
//这里拆分说明:
// //获取流
// Stream<People> streams = list.stream();
// //排序,传入自定义的比较器,比较规则是people的age值
// streams.sorted(Comparator.comparing(People::getAge));
// //循环迭代
// streams.forEach(people -> System.out.println(people));
//Java 8 中我们可以通过 "::" 关键字来访问类的构造方法,对象方法,静态方法。
list.stream().sorted(Comparator.comparing(People::getAge)).forEach(people -> System.out.println(people));
System.out.println("查看原集合,注意stream的特性");
//原list集合是不会改变的
for (People people : list) {
System.out.println(people);
}
}
4、filter()方法,主要用来过滤出特定条件下的子集合
/**
* filter方法的使用
*/
public void filterEle() {
// 输出年龄大于8的人
System.out.println("-----过滤前-----");
list.forEach(people -> System.out.println(people));
System.out.println("-----过滤后-----");
// java 8 前
System.out.println("java 8 前");
for(People people: list){
if (people.getAge() > 8) {
System.out.println(people);
}
}
// java 8 stream
System.out.println("java 8 stream");
list.stream().filter((People people) -> people.getAge() > 8).forEach(people-> System.out.println(people));
}
5、anyMatch,判断集合中是否有任意一个满足条件
/**
* anyMatch,判断集合中是否有任意一个满足条件
*/
public void anyMatchEle() {
//判断集合中是否存在年龄==8的学生
// java 8 前
System.out.println("java 8 前");
boolean flag = false;
for (People people : list) {
if(people.getAge()==8) {
flag = true;
break;
}
}
System.out.println("是否存在年龄等于8的人:"+flag);
System.out.println("java 8 stream");
// java 8 stream
flag = list.stream().anyMatch(people->people.age==8);
System.out.println("是否存在年龄等于8的人:"+flag);
}
6、allMatch,判断集合中是否都满足条件,和anyMatch是个互斥操作
/**
* allMatch,判断集合中是否都满足条件
*/
public void allMatchEle() {
// 判断集合中是否所有人年龄大于2
// java 8 前
System.out.println("java 8 前");
boolean flag = true;
for (People people : list) {
if(people.getAge()<=2) {
flag = false;
break;
}
}
System.out.println("判断集合中是否所有人年龄大于2:"+flag);
System.out.println("java 8 stream");
// java 8 stream
flag = list.stream().allMatch(people->people.age>2);
System.out.println("判断集合中是否所有人年龄大于2:"+flag);
}
7、limit():使用该方法截断
/**
* limit 截断.
*/
public void limitEle() {
// 从第三个开始截断,只输出前三个
System.out.println("-----截断前-----");
list.forEach(user -> System.out.println(user));
System.out.println("-----截断后-----");
// java 8 前
System.out.println("java 8 前");
for (int i = 0; i < 3; i++) {
System.out.println(list.get(i));
}
// java 8 stream
System.out.println("java 8 stream");
list.stream().limit(3).forEach(people -> System.out.println(people));
}
8、skip():与limit互斥,使用该方法跳过元素
/**
* skip 跳过.
*/
public void shipEle() {
// 跳过前三个元素,从第四个开始输出
System.out.println("-----跳过前-----");
list.forEach(user -> System.out.println(user));
System.out.println("-----跳过后-----");
// java 8 前
System.out.println("java 8 前");
for (int i = 3; i < list.size(); i++) {
System.out.println(list.get(i));
}
// java 8 stream
System.out.println("java 8 stream");
list.stream().skip(3).forEach(people -> System.out.println(people));
}
9、distinct():使用该方法去重,注意:必须重写对应泛型的hashCode()和equals()方法
/**
* distinct 去重.
*/
public void distinctEle() {
// 为list去除重复数据
System.out.println("-----去重前-----");
list.forEach(user -> System.out.println(user));
System.out.println("-----去重后-----");
// java 8 前
System.out.println("java 8 前,本人一般的做法是转存到set集合");
Set<People> sets = new HashSet<Test.People>();
for (People people : list) {
sets.add(people);
}
for (People people : sets) {
System.out.println(people);
}
// java 8 stream
System.out.println("java 8 stream");
list.stream().distinct().forEach(user -> System.out.println(user));
}
10、map():接收一个方法作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
/**
* map():接收一个方法作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
*/
public void mapEle() {
//获取所有人的年龄集合
// java 8 前
System.out.println("java 8 前");
List<Integer> ages = new ArrayList<Integer>();
for (People people : list) {
ages.add(people.getAge());
}
ages.forEach(age->System.out.println(age));
System.out.println("java 8 stream");
// java 8 stream
ages = list.stream().map(people->people.getAge()).collect(Collectors.toList());
ages.forEach(age->System.out.println(age));
}
11、flatMapEle() 将一个stream里面的集合元素展开,使用展开之后的元素组合成一个新的stream,其实说白了就是集合合并
/**
* flatMapEle() 将一个stream里面的集合元素展开,使用展开之后的元素组合成一个新的stream,其实说白了就是集合合并
*/
public void flatMapEle() {
List<People> list2 = Arrays.asList(new People("李四", 10),new People("王五", 10));
//将list1和list2合并成一个新的集合
// java 8 前
System.out.println("java 8 前");
List<People> newList = new ArrayList<Test.People>();
for (People people : list) {
newList.add(people);
}
for (People people : list2) {
newList.add(people);
}
newList.forEach(people->System.out.println(people));
// java 8 stream
newList = new ArrayList<Test.People>();
System.out.println("java 8 stream");
newList = Stream.of(list,list2).flatMap(newPeoples->newPeoples.stream()).collect(Collectors.toList());
newList.forEach(people->System.out.println(people));
}
12、计算统计
/**
* 计算统计
*/
public void numEle() {
//前面我们知道了map方法,map返回的是Stream,如果我们知道数据类型,让偶们可以采用mapToInt返回IntStream进行一些数据计算操作
//IntSummaryStatistics主要用于统计整形数组中元素的最大值,最小值,平均值,个数,元素总和等等
IntSummaryStatistics num = list.stream().mapToInt(u -> u.getAge()).summaryStatistics();
System.out.println("总共人数:" + num.getCount());
System.out.println("平均年龄:" + num.getAverage());
System.out.println("最大年龄:" + num.getMax());
System.out.println("最小年龄:" + num.getMin());
System.out.println("年龄之和:" + num.getSum());
}