什么是stream
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
创建stream
创建stream共5种方式
- 通过数组创建
- 通过列表创建
- Stream.generate()
- Stream.iterate()
- Stream.of()
通过数组创建
// 通过数组创建
int[] arr = new int[]{1,2,3,4,5,6,7,8};
Arrays.stream(arr).filter(i->i%2==0).forEach(System.out::println);
通过列表创建
// 通过 list 创建
Arrays.asList(1,2,3,4,5,6,7,8).stream().filter(s->s%2==0).forEach(System.out::println);
Stream.generate()
会生成一个无限的stream,通常和
limit
方法配合使用
// Stream.generate()
Stream.generate(()->1).limit(5).forEach(System.out::println);
Stream.iterate()
会生成一个无限的 stream,第一个参数为首项,也是通常和
limit
方法配合使用
// Stream.iterate()
Stream.iterate(0,i->i+1).limit(5).forEach(System.out::println);
Stream.of()
//Stream.of()
Stream.of(1,2,3,4,5,6).limit(3).forEach(System.out::println);
通过stream将list转换为set
package jdk8.stream;
import java.util.ArrayList;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 员工类 name,age,job
*/
class Employee{
private String name;
private Integer age;
private String job;
Employee(String name, Integer age, String job) {
this.name = name;
this.age = age;
this.job = job;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", job='" + job + '\'' +
'}';
}
}
public class CreateStreamTest {
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("luckyFang",22,"全栈工程师"));
employees.add(new Employee("alice",20,"程序员鼓励师"));
employees.add(new Employee("zhangsan",35,"安保队长"));
employees.add(new Employee("zhangsan",35,"安保队长"));
/**
* 创建流:
* - 串行流 单线程
* - 并行流 多线程 (有效率优势)
*/
// 串行流
employees.stream();
// 并行流
employees.parallelStream();
// 将 list 转换为 set集合
Set<Employee> collect = employees.stream().collect(Collectors.toSet());
// 遍历 set
collect.forEach(ele-> System.out.println(ele));
/**
* 思考问题: set 特性是 无序不重复集合
* 但是查看输出结果发现了重复了 可见 我们转set失败了
* 为什么会失败?
*/
}
}
Employee{name='zhangsan', age=35, job='安保队长'}
Employee{name='luckyFang', age=22, job='全栈工程师'}
Employee{name='alice', age=20, job='程序员鼓励师'}
Employee{name='zhangsan', age=35, job='安保队长'}
思考问题: set 特性是 无序不重复
集合
但是查看输出结果发现了重复了
可见 我们转set失败了
为什么会失败?
这里就涉及到java基础知识了,虽然对象属性是相等的,但是我们每次new对象的地址是不一样的,所以我们需要重写equals
方法,和hashCdoe
方法。
为什么需要这样做?
如果集合要添加元素时,会先调用这个元素的
hashCode
方法,来决定要存储的位置,如果这个位置没有元素则直接存储,如果有元素,则需要调用equals
方法来判断是否是同一个对象,如果相同则不存,不相同就散列它的地址.
- 如果两个java对象相同,那么它的
hashCode
一定相同。 - 如果两个对象的
hashCode
相同那么他们并不一定相同
class Employee{
private String name;
private Integer age;
private String job;
Employee(String name, Integer age, String job) {
this.name = name;
this.age = age;
this.job = job;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(name, employee.name) && Objects.equals(age, employee.age) && Objects.equals(job, employee.job);
}
@Override
public int hashCode() {
return Objects.hash(name, age, job);
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", job='" + job + '\'' +
'}';
}
}
然后就解决了对象重复问题.
通过stream将list转换为map
public static void listToMap(){
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("luckyFang",22,"全栈工程师"));
employees.add(new Employee("alice",20,"程序员鼓励师"));
employees.add(new Employee("zhangsan",35,"安保队长"));
/**
* Fun1:
* param1: key
* Fun2:
* param2: val
*/
employees.stream().collect(Collectors.toMap(new Function<Employee, String>() {
@Override
public String apply(Employee employee) {
return employee.getName();
}
}, new Function<Employee, Employee>() {
@Override
public Employee apply(Employee employee) {
return employee;
}
})).forEach(new BiConsumer<String, Employee>() {
@Override
public void accept(String s, Employee employee) {
System.out.printf("%s:%s\n",s,employee);
}
});
}
reduce实现求和
这里通过
reduce
操作实现求和,需要注意的是返回值类型要和列表类型一致。
reduce: 通过计算模型将stream中所有的值计算最终结果
public static void streamReduceTest() {
// 创建stream
Stream<Integer> integerStream = Stream.of(10, 50, 30, 10);
// reduce:通过计算模型将stream中的值计算为一个最终的结果
Optional<Integer> reduce = integerStream.reduce((integer, integer2) -> integer + integer2);
// Optional包装对象 通过.get() 访问
System.out.println(reduce.get());
// 员工类 年龄相加
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("luckyFang", 22, "全栈工程师"));
employees.add(new Employee("alice", 20, "程序员鼓励师"));
employees.add(new Employee("zhangsan", 35, "安保队长"));
// 通过stream 将所有员工姓名相加
Optional<Employee> ageSum = employees.stream().reduce((e1, e2) -> new Employee("ageSum", e1.getAge() + e2.getAge(), "null"));
System.out.println(ageSum.get());
}
Employee{name='ageSum', age=77, job='null'}
max和min找最大最小值
public static void streamMaxOrMInTest() {
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("luckyFang", 22, "全栈工程师"));
employees.add(new Employee("alice", 20, "程序员鼓励师"));
employees.add(new Employee("zhangsan", 35, "安保队长"));
// 找到所有员工中年龄最大的
Optional<Employee> max = employees.stream().max((o1, o2) -> o1.getAge() - o2.getAge());
System.out.println(max.get());
// 找到所有员工中年龄最小的
Optional<Employee> min = employees.stream().min((o1, o2) -> o1.getAge() - o2.getAge());
System.out.println(min.get());
}
Employee{name='zhangsan', age=35, job='安保队长'}
Employee{name='alice', age=20, job='程序员鼓励师'}
match实现条件筛选
anyMatch: 任一匹配
allMatch: 全部匹配
public static void matchTest(){
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("luckyFang", 22, "全栈工程师"));
employees.add(new Employee("alice", 20, "程序员鼓励师"));
employees.add(new Employee("zhangsan", 35, "安保队长"));
// anyMatch 任意匹配
// 查看员工中有没有小于18岁的
System.out.println(employees.stream().anyMatch(new Predicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getAge() < 18;
}
}));
// allMatch 全部匹配
// 查看所有员工是不是大于18岁
System.out.println(employees.stream().allMatch(new Predicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getAge() > 18;
}
}));
}
false
true
filter过滤器
filter:过滤器,通过断言决定取舍,满足条件的则保留,不满足条件的抛去.
public static void filterTest() {
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("luckyFang", 22, "全栈工程师"));
employees.add(new Employee("alice", 20, "程序员鼓励师"));
employees.add(new Employee("zhangsan", 35, "安保队长"));
// 查找所有员工中年龄小于 30岁的
employees.stream().filter(new Predicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getAge() < 30;
}
}).forEach(System.out::println);
}
Employee{name='luckyFang', age=22, job='全栈工程师'}
Employee{name='alice', age=20, job='程序员鼓励师'}
limit和skip
limit:最大限制(几条数据)
skip: 跳过(第几条数据)
注意:初始下标从1开始
public static void limitTest() {
System.out.println("===========");
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("luckyFang", 22, "全栈工程师"));
employees.add(new Employee("alice", 20, "程序员鼓励师"));
employees.add(new Employee("zhangsan", 35, "安保队长"));
// 代表从头到尾取两条数据
employees.stream().limit(2).forEach(System.out::println);
// skip 跳过
System.out.println("======");
// 跳过第一条数据 后取两条数据
employees.stream().skip(1).limit(2).forEach(System.out::println);
}
}
======
Employee{name='luckyFang', age=22, job='全栈工程师'}
Employee{name='alice', age=20, job='程序员鼓励师'}
======
Employee{name='alice', age=20, job='程序员鼓励师'}
Employee{name='zhangsan', age=35, job='安保队长'}
sorted实现排序
public static void sortTest(){
System.out.println("===========");
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("luckyFang", 22, "全栈工程师"));
employees.add(new Employee("alice", 20, "程序员鼓励师"));
employees.add(new Employee("zhangsan", 35, "安保队长"));
// 实现排序
employees.stream().sorted((o1, o2) -> o1.getAge()-o2.getAge()).forEach(System.out::println);
}
Employee{name='alice', age=20, job='程序员鼓励师'}
Employee{name='luckyFang', age=22, job='全栈工程师'}
Employee{name='zhangsan', age=35, job='安保队长'}
综合实战
public static void demoTest() {
System.out.println("\n\n\n");
ArrayList<Employee> employees = new ArrayList<>();
employees.add(new Employee("zhangsan", 30, "后端工程师"));
employees.add(new Employee("lisi", 27, "嵌入式工程师"));
employees.add(new Employee("wangwu", 21, "实习生"));
employees.add(new Employee("zhaoliu", 37, "架构师"));
// 说明 按照年龄降序排列所有工程师
employees.stream().sorted((o1, o2) -> o2.getAge() - o1.getAge()).filter(o -> o.getJob().contains("工程师")).forEach(System.out::println);
}
Employee{name='zhangsan', age=30, job='后端工程师'}
Employee{name='lisi', age=27, job='嵌入式工程师'}
并行流
分开任务去执行
public static void parallelTest() {
// 串行流求和
Instant now1 = Instant.now();
long sum = 0;
for (long i = 0; i < 500000000L; i++) {
sum += i;
}
System.out.println("正常求和:" + Duration.between(now1, Instant.now()).toMillis());
// 并行流求和
Instant now = Instant.now();
// 50亿数据
LongStream longStream = LongStream.rangeClosed(0, 500000000L);
OptionalLong reduce = longStream.parallel().reduce((l, r) -> l + r);
System.out.println("并行求和:" + Duration.between(now, Instant.now()).toMillis());
}
正常求和:340
并行求和:199