为了能够让工程师更方便地处理集合数据,Java8 中新增了一大特性,那就是 Stream (也就是流,也叫做流式计算)。利用新提供的 Stream API,可以让 Java 以声明性地迭代方式处理集合,它也类似于真正的流水线生产那样:流 = 工序 + 节点 + 数据。
流的注意事项:
为了更直观的比对传统集合或者数组操作的方式以及流式计算的方式,做了如下的准备工作:
定义Author类:
package StreamAPI;
import java.util.List;
@EqualsAndHashCode
public class Author {
private long id;
private String name;
private Integer age;
private String intro;
private List books;
public Author(long id, String name, Integer age, String intro, List books) {
this.id = id;
this.name = name;
this.age = age;
this.intro = intro;
this.books = books;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getIntro() {
return intro;
}
public void setIntro(String intro) {
this.intro = intro;
}
public List getBooks() {
return books;
}
public void setBooks(List books) {
this.books = books;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Author author = (Author) o;
return id == author.id && Objects.equals(name, author.name) && Objects.equals(age, author.age) && Objects.equals(intro, author.intro) && Objects.equals(books, author.books);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age, intro, books);
}
定义Book类:
package StreamAPI;
@EqualsAndHashCode
public class Book {
private long id;
private String name;
private String category;
private Integer score;
private String intro;
public Book(long id, String name, String category, Integer score, String intro) {
this.id = id;
this.name = name;
this.category = category;
this.score = score;
this.intro = intro;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return id == book.id && Objects.equals(name, book.name) && Objects.equals(category, book.category) && Objects.equals(score, book.score) && Objects.equals(intro, book.intro);
}
@Override
public int hashCode() {
return Objects.hash(id, name, category, score, intro);
}
}
测试类:
package StreamAPI;
import com.company.Task4_3;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class StreamDemo {
public static void main(String[] args) {
}
private static List getAuthors(){
//数据初始化
Author author = new Author(1L,"蒙多",33,"一个从菜刀中悟哲理的祖安人",null);
Author author2 = new Author(2L,"亚拉索",15,"狂风也追逐不上他的思考速度",null);
Author author3 = new Author(3L,"易",14,"是这个世界在限制他的思维",null);
Author author4 = new Author(3L,"易",14,"是这个世界在限制他的思维",null);
//书籍列表
List books1 = new ArrayList<>();
List books2 = new ArrayList<>();
List books3 = new ArrayList<>();
books1.add(new Book(1L,"刀的两侧是光明与黑暗","哲学,爱情",88,"用一把刀分割了爱恨"));
books1.add(new Book(2L,"一个人不能死在同一把刀下","个人成长,爱情",99,"讲述失恶"));
books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"哲学思想"));
books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"哲学思想"));
books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"哲学思想"));
books2.add(new Book(4L,"吹或不吹","爱情,个人传记",56,"哲学"));
books3.add(new Book(5L,"你的剑就是我的剑","爱情",56,"剑"));
books3.add(new Book(6L,"风与剑","个人传记",100,"两个哲学家"));
books3.add(new Book(6L,"风与剑","个人传记",100,"两个哲学家"));
author.setBooks(books1);
author2.setBooks(books2);
author3.setBooks(books3);
author4.setBooks(books3);
List authorList = new ArrayList<>(Arrays.asList(author,author2,author3,author4));
return authorList;
}
}
现在要获取打印年龄小于18岁的作家的名字,利用StreamAPI我们可以采用以下的方式进行打印:
public static void main(String[] args) {
List authors = getAuthors();
authors.stream()//把集合转换成流
.distinct()//去除重复元素
.filter(author->author.getAge()<18) //过滤符合条件的作者
.forEach(author->System.out.println(author.getName()));
}
单列集合:集合对象.stream()
List authors = getAuthors();
Stream stream = authors.stream();
数组:Arrays.stream(数组)或者Stream.of来创建
Integer[] arr = {1,2,3,4,5};
Stream stream = Arrays.stream(arr);
Stream stream2 = Stream.of(arr);
双列集合:转换成单列集合再创建
Map map = new HashMap<>();
map.put("蜡笔小新",19);
map.put("黑子",17);
map.put("日向翔阳",16);
Stream> stream = map.entrySet().stream();
可以对流中的元素进行条件过滤,符合过滤条件的才能够继续留在流中。
例如:输出名字长度超过1的作家名字
List authors = getAuthors();
authors.stream()//把集合转换成流
.filter(author->author.getName().length()>1)
.forEach(author->System.out.println(author.getName()));
可以对流中的元素进行计算或转换
如:打印所有作家的名字,或者为了方便之后的操作,将集合转换为字符串类型
List authors = getAuthors();
authors.stream()
.map(new Function() {
@Override
public String apply(Author author) {
return author.getName();
}
})
.forEach(s->System.out.println(s));
可以去除流中重复的元素,是依赖Object的equals方法来判断是否是相同对象的。所有要注意需要重写equals方法
如打印所有作家的名字,其中不能有重复元素
List authors = getAuthors();
authors.stream()
.distinct()
.forEach(author->System.out.println(author.getName()));
可以对流中的元素进行排序,要实现Comparable接口
如对流中的元素按照年龄进行降序排列,并且要求不能有重复的元素。
第一种方式:调用无参的sorted()方法,因为要实现Comparable接口,因此先修改Author类的代码:
package StreamAPI;
import lombok.EqualsAndHashCode;
import java.util.List;
import java.util.Objects;
@EqualsAndHashCode
public class Author implements Comparable {
private long id;
private String name;
private Integer age;
private String intro;
private List books;
@Override
public int compareTo(Author o) {
return o.getAge() - this.getAge();
}
}
再对元素按照年龄降序排列:
public static void main(String[] args) {
List authors = getAuthors();
authors.stream()
.distinct()
.sorted()
.forEach(author->System.out.println(author.getName()));
}
第二种:直接调用有参的sorted()方法,不需要实现Comparable接口:
List authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1,o2)->o2.getAge() - o1.getAge())
.forEach(author->System.out.println(author.getName()));
可以设置流的最大长度,超出的部分将被抛弃
如对流中的元素按照年龄降序排列,并且要求不能有重复的元素,然后打印其中年龄最大的两个元素
List authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1,o2)->o2.getAge() - o1.getAge())
.limit(2)
.forEach(author->System.out.println(author.getName()));
跳过流中的n个元素,返回剩下的元素
如打印出了年龄最大的作家外的其他作家,要求不能有重复元素,并且按照年龄降序排列
List authors = getAuthors();
authors.stream()
.distinct()
.sorted((o1,o2)->o2.getAge() - o1.getAge())
.skip(1)//跳过第一个
.forEach(author->System.out.println(author.getName()));
map只能把一个对象转换成另一个对象在作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素
如:打印所有书籍的名字,要求对重复元素进行去重
List authors = getAuthors();
authors.stream()
.flatMap(new Function>() {
@Override
public Stream apply(Author author) {
return author.getBooks().stream();
}
})
.distinct()
.forEach(book->System.out.println(book.getName()));
如:打印现有数据的所有分类,要求对分类进行去重,不能出现这种格式:哲学,爱情
List authors = getAuthors();
authors.stream()
.flatMap(author->author.getBooks().stream())
.distinct()
.flatMap(book->Arrays.stream(book.getCategory().split(",")))
.distinct()
.forEach(category->System.out.println(category));
对流中的元素进行遍历操作,我们通过传入的参数去指定对遍历到的元素进行什么具体操作
可以用来获取当前流中的元素个数
如:打印这些作家的所有书籍的数目,注意删除重复元素
List authors = getAuthors();
long count = authors.stream()
.flatMap(author->author.getBooks().stream())
.distinct()
.count();
System.out.println(count);
可以获取流中的最值
如:获取这些作家书籍的最高分和最低分
Stream< Author > --> Stream< Book > --> Stream< Integer >(分数的类型是int)
List authors = getAuthors();
Optional max = authors.stream()
.flatMap(author->author.getBooks().stream())
.map(book->book.getScore())
.max((score1,score2)->score1-score2);
Optional min = authors.stream()
.flatMap(author->author.getBooks().stream())
.map(book->book.getScore())
.min((score1,score2)->score1-score2);
System.out.println(min.get());
System.out.println(max.get());
把当前流转换成一个集合
如:获取一个存放所有作者名字的List集合
List authors = getAuthors();
List nameList = authors.stream()
.map(author->author.getName())
.collect(Collectors.toList());
System.out.println(nameList);
如:获取一个所有书名的Set集合
Set books = authors.stream()
.flatMap(author->author.getBooks().stream())
.collect(Collectors.toSet());
System.out.println(books);
如:获取一个Map集合,map的key为作者名,value为List< Book >
Map> map = authors.stream()
.distinct()
.collect(Collectors.toMap(author->author.getName(),author->author.getBooks()));
anyMatch: 用来判断是否有任意符合匹配条件的元素,结果为boolean类型
如:判断是否有年龄29以上的作家
List authors = getAuthors();
boolean flag = authors.stream()
.anyMatch(author->author.getAge()>29);
System.out.println(flag);
allMatch: 用来判断是否都符合匹配条件
noneMatch: 判断是否都不符合匹配条件
findAny: 获取流中任意一个元素,该方法没有办法保证获取的一定是流中的哪一个元素
findFirst: 获取流中的第一个元素
将stream流中的元素组合起来,我们可以传入一个初始值,它会按照我们的计算方式一次拿流中的元素和在初始化值的基础上进行计算,计算结果再和后面的元素计算。
它内部的计算方式如下:
T result = identity;
for(T element : this stream)
result = accumulator.apply(result,element)
return result;
其中identity就是我们可以通过方法参数传入的初始值,accumulator的apply具体进行什么计算也是我们通过方法参数来确定的
如:求所有作者年龄之和
List authors = getAuthors();
Integer sum = authors.stream()
.distinct()
.map(author->author.getAge())
.reduce(0,(result,element)->result+element);//初始值为0,result为最终变量,element为遍历的元素值
System.out.println(sum);
现有Employee实体类:
package StreamAPI;
public class Employee {
public enum Type {MANAGER,SELLER,OFFICER};
private String name;
private String genger;
private Integer age;
private boolean married;
private Type type;
public Employee(String name, String genger, Integer age, boolean married, Type type) {
this.name = name;
this.genger = genger;
this.age = age;
this.married = married;
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGenger() {
return genger;
}
public void setGenger(String genger) {
this.genger = genger;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public boolean isMarried() {
return married;
}
public void setMarried(boolean married) {
this.married = married;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", genger='" + genger + '\'' +
", age=" + age +
", married=" + married +
", type=" + type +
'}';
}
}
Company实体类:
package StreamAPI;
import java.util.List;
public class Company {
public enum Type{BIG,SMALL};
private String name;
private Type type;
private List employee;
public Company(String name, Type type, List employee) {
this.name = name;
this.type = type;
this.employee = employee;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
public List getEmployee() {
return employee;
}
public void setEmployee(List employee) {
this.employee = employee;
}
@Override
public String toString() {
return "Company{" +
"name='" + name + '\'' +
", type=" + type +
", employee=" + employee +
'}';
}
}
再给集合插入以下数据:
List> list = new ArrayList<>();
List employees = Arrays.asList(
new Employee("张勇","男",28,true,Employee.Type.MANAGER),
new Employee("李强","男",22,false,Employee.Type.SELLER),
new Employee("王五","男",32,false,Employee.Type.SELLER),
new Employee("梅丽","女",26,true,Employee.Type.OFFICER),
new Employee("郑帅","男",29,false,Employee.Type.OFFICER),
new Employee("曾美","女",27,true,Employee.Type.SELLER),
new Employee("郝俊","男",22,true,Employee.Type.SELLER),
new Employee("方圆","女",24,false,Employee.Type.SELLER)
);
Company moubao = new Company("某宝",Company.Type.BIG,employees);
employees = Arrays.asList(
new Employee("吴琼","女",27,true,Employee.Type.SELLER),
new Employee("陈辰","女",28,false,Employee.Type.OFFICER),
new Employee("刘能","男",35,true,Employee.Type.OFFICER),
new Employee("周七","男",29,false,Employee.Type.OFFICER),
new Employee("汪旺","男",21,false,Employee.Type.OFFICER),
new Employee("胡涂","男",27,false,Employee.Type.OFFICER),
new Employee("杨茂","男",34,true,Employee.Type.MANAGER),
new Employee("朱坚","男",30,false,Employee.Type.MANAGER)
);
Company mouxin = new Company("某东",Company.Type.BIG,employees);
employees = Arrays.asList(
new Employee("冯过","男",35,false,Employee.Type.SELLER),
new Employee("何花","女",27,false,Employee.Type.MANAGER),
new Employee("卫精","男",25,true,Employee.Type.OFFICER),
new Employee("施工","男",28,false,Employee.Type.OFFICER),
new Employee("沈月","女",24,false,Employee.Type.OFFICER),
new Employee("乐欢","女",22,false,Employee.Type.OFFICER),
new Employee("安全","男",33,true,Employee.Type.MANAGER),
new Employee("林森","男",26,true,Employee.Type.SELLER)
);
Company wahaha = new Company("某哈哈",Company.Type.SMALL,employees);
//加入列表
list.add(moubao);
list.add(mouxin);
list.add(wahaha);
实现如下需求:统计公司类型为 BIG 的所有未婚员工,同时按年龄排序,并且未婚员 工不区分公司,也就是最终结果放在一个列表中(提示:整个需求可以用一行代码完成)
List employeeList = list.stream()
.filter(conpany->conpany.getType() == Company.Type.BIG)
.flatMap(company->company.getEmployee().stream())
.filter(employee -> employee.isMarried() == false)
.sorted((o1,o2)->o1.getAge() - o2.getAge() )
.collect(Collectors.toList());
for(Employee e : employeeList)
System.out.println(e.toString());