1.为什么要使用Stream API?
实际开发中,需要操作的数据大都来自于mysql或Oracle等关系数据库,还有的数据库如Redis、MongDB,对于这些非关系数据库的操作就需要Java层面处理(Stream API)。
2.Stream和Collection集合的区别?
Collectio是一种静态的数据内存结构,面向内存,存储在内存中;而Stream是有关计算的,面向CPU,通过CPU计算处理。
3.什么是Stream API?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,Stream讲的是计算!”
注意:
① stream自己不会存储元素;
② Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream;
③ Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
4.Stream操作的三步骤
(1)创建Stream
一个数据源(如:集合、数组),获取一个流
(2)中间操作
一个中间操作链(多个操作),对数据源的数据进行处理
(3)终止操作(终端操作)
一旦执行终止操作,就执行中间操作链,并产生结果。之后不会再被使用
(1)创建Stream的四种方式:
@Test public void test01(){ //创建Stream方式一:使用集合 List<String> list = Arrays.asList("北京", "上海", "广州", "深圳"); //default Stream<E> stream():返回一个顺序流 Stream<String> stream = list.stream(); //default Stream<E> parallelStream():返回一个并行流 Stream<String> stringStream = list.parallelStream(); //创建Stream方式二:使用数组 String[] arr = new String[]{"Java","Python","PHP","C++","GO"}; //调用Arrays的public static <T> Stream<T> stream(T[] array):返回一个流 Stream<String> stream1 = Arrays.stream(arr); //创建Stream方式三:使用Stream的of() Stream<Integer> stream2 = Stream.of(12, 5, 8, 46); //创建stream方式四:创建无限流(了解) //迭代(遍历前十个偶数) //public static<T> Stream<T> iterate(final T seed,final Unaryoperator<T> f) Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println); //生成 //public static<T>Stream<T> generate(Supplier<T> s) Stream.generate(Math::random).limit(10).forEach(System.out::println); }
(2)Stream的中间操作
//User类只是一个JavaBean public class User { private int id; private String name; private int age; private double salary; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public User() { } public User(int id, String name, int age, double salary) { this.id = id; this.name = name; this.age = age; this.salary = salary; } @Override public String toString() { return "User{" + "id='" + id + '\'' + "name='" + name + ", age=" + age + ", salary=" + salary + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return id == user.id && age == user.age && Double.compare(user.salary, salary) == 0 && Objects.equals(name, user.name); } @Override public int hashCode() { return Objects.hash(id, name, age, salary); } public static List<User> getUsers(){ ArrayList<User> users = new ArrayList<>(); users.add(new User(1001,"马云",36,600.05)); users.add(new User(1002,"乔布斯",34,800.23)); users.add(new User(1003,"马化腾",31,599.35)); users.add(new User(1004,"雷军",25,450.21)); users.add(new User(1005,"任正非",46,756.35)); users.add(new User(1006,"比尔盖茨",50,960.87)); users.add(new User(1007,"李彦宏",26,630.31)); users.add(new User(1008,"扎克伯格",18,265.45)); users.add(new User(1008,"扎克伯格",18,265.45)); return users; } }
① 筛选与切片
//1.筛选与切片 @Test public void test01(){ List<User> users = User.getUsers(); Stream<User> stream = users.stream(); //1.查询User的salary大于700的User信息 //filter(Predicate p)——接收Lambda:从流中过滤某些元素 stream.filter(u -> u.getSalary() > 700).forEach(System.out::println); System.out.println("--------------------------------------------------"); //2.limit(n)——截断流:使其输出的元素不超过指定数量 users.stream().limit(2).forEach(System.out::println); System.out.println("--------------------------------------------------"); //3.skip(n)——跳过元素:返回一跳过前面的n个元素的流,若不足,返回一个空流 users.stream().skip(5).forEach(System.out::println); System.out.println("--------------------------------------------------"); //4.distinct()——去重:通过流生成的hashcode()和equals()去除重复元素 users.stream().distinct().forEach(System.out::println); }
② 映射
//2.映射 @Test public void test02(){ //map(Function f):接受一个函数作为参数,将函数转换成其他提取信息,该函数会被应用于每个元素上,并将其映射成一个新的元素 List<String> list = Arrays.asList("GG", "BB", "MM", "DD"); //将集合list中的元素转换为小写并遍历输出 list.stream().map(str -> str.toLowerCase()).forEach(System.out::println); //练习:查找User中名字大于三位的User List<User> users = User.getUsers(); Stream<String> nameStream = users.stream().map(User::getName); nameStream.filter(name -> name.length()>3).forEach(System.out::println); //flatMap(Function f):接收一个函数作为参数,将流中的每个值转换为另一个流,然后把所有的流连接成一个流 Stream<Character> characterStream = list.stream().flatMap(StreamTest::stringToStream); characterStream.forEach(System.out::println); } //将字符串中的多个字符构成的集合转换为对应的Stream的实例 public static Stream<Character> stringToStream(String str){ ArrayList<Character> list = new ArrayList<>(); for(Character ch : str.toCharArray()){ list.add(ch); } return list.stream(); }
③ 排序
//3.排序 @Test public void test03(){ //数组排序 List<Integer> list = Arrays.asList(18, 25, 36, 22, 10,-2); list.stream().sorted().forEach(System.out::println); // //抛异常java.lang.ClassCastException,原因User未实现Comparable接口 // List<User> users = User.getUsers(); // users.stream().sorted().forEach(System.out::println);//自然排序 //对象排序 List<User> users = User.getUsers(); users.stream().sorted((u1, u2) -> { int ageValue = Integer.compare(u1.getAge(), u2.getAge());//age正序 if(ageValue!=0){ return ageValue; }else{ return Double.compare(u1.getSalary(),u2.getSalary());//salary正序 } }).forEach(System.out::println); System.out.println(); }
(2)Stream的终止操作
① 匹配与查找
//① 匹配与查找 @Test public void test01(){ List<User> users = User.getUsers(); //allMatch(Predicate p)——检查是否匹配所有元素。练习:是否所有的员工的年龄都大于20 boolean b = users.stream().allMatch(u -> u.getAge() > 20); System.out.println(b);//false System.out.println("--------------------------------------------------"); //anyMatch(Predicate p)——检查是否至少匹配一个元素。练习:是否存在员工的工资大于500 boolean b1 = users.stream().anyMatch(u -> u.getSalary() > 500); System.out.println(b1); System.out.println("--------------------------------------------------"); //noneMatch(Predicate p)——检查是否没有匹配的元素。练习:是否存在员工姓"雷” boolean b2 = users.stream().noneMatch(u -> u.getName().startsWith("任")); System.out.println(b2); System.out.println("--------------------------------------------------"); //findFirst——返回第一个元素 Optional<User> first = users.stream().findFirst(); System.out.println(first); System.out.println("--------------------------------------------------"); //findAny——返回当前流中的任意元素 Optional<User> any = users.stream().findAny(); System.out.println(any); System.out.println("--------------------------------------------------"); //count——返回流中元素的总个数 long count = users.stream().count(); System.out.println(count); System.out.println("--------------------------------------------------"); //max (Comparator c)——返回流中最大值 练习:返回最高的工资 Stream<Double> doubleStream = users.stream().map(u -> u.getSalary()); Optional<Double> max = doubleStream.max(Double::compare); System.out.println(max); System.out.println("--------------------------------------------------"); //min( Comparator c)——返回流中最小值 练习:返回最低工资的员工 Optional<User> minUser = users.stream().min((u1, u2) -> Double.compare(u1.getSalary(), u2.getSalary())); System.out.println(minUser); System.out.println("--------------------------------------------------"); //forEach(Consumer c)——内部迭代 users.stream().forEach(System.out::println); System.out.println("--------------------------------------------------"); // //使用集合的遍历操作 // users.forEach(System.out::println); }
② 归约
//② 归约 @Test public void test02(){ //reduce(T identity,Binaryoperator)——可以将流中元素反复结合起来,得到一个值,返回一个T。练习1:计算1-10的自然数的和 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Integer sum = list.stream().reduce(0, Integer::sum);//第一个参数是初始值 System.out.println(sum); //reduce(Binaryoperator)——可以将流中元素反复结合起来,得到一个值。返回Optional<T> 练习2:计算公司所有员工工资的总和 List<User> users = User.getUsers(); Stream<Double> doubleStream = users.stream().map(User::getSalary); // Optional<Double> sumSalary = doubleStream.reduce(Double::sum);//Double中的public static double sum(double a, double b) Optional<Double> sumSalary = doubleStream.reduce((d1, d2) -> d1 + d2); System.out.println(sumSalary); }
③ 收集
//③ 收集 @Test public void test03(){ //collect(collector c)-将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中的元素做汇总的方法 //练习1:查找工资大于700的员工,结果返回为一个List或Set List<User> users = User.getUsers(); List<User> userList = users.stream().filter(u -> u.getSalary() > 700).collect(Collectors.toList()); userList.forEach(System.out::println); System.out.println("------------------------------------------------"); Set<User> userSet = users.stream().filter(u -> u.getSalary() > 700).collect(Collectors.toSet()); userSet.forEach(System.out::println); }
Optional< T >类(java.util.Optional)是一个容器类, 为了避免避免空指针异常而出现的。
Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
public class Boy { private Girl girl; public Boy() { } public Boy(Girl girl) { this.girl = girl; } public Girl getGirl() { return girl; } public void setGirl(Girl girl) { this.girl = girl; } @Override public String toString() { return "Boy{" + "girl=" + girl + '}'; } } class Girl { private String name; public Girl() { } public Girl(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Girl{" + "name='" + name + '\'' + '}'; } }
@Test public void test01(){ Girl girl = new Girl(); //Optional.of(T t) :创建一个Optional实例,t必须非空 //Optional.empty() : 创建一个空的Optional实例 Optional<Girl> girl1 = Optional.of(girl); System.out.println(girl1); //Optional.ofNullable(T t): t可以为null girl = null; Optional<Girl> girlOptional = Optional.ofNullable(girl); System.out.println(girlOptional); //orElse(T t1):如果单前的optional内部封装的t是非空的,则返回内部的t.如果内部的是空的,则返回orElse()方法中的参数t1. Girl girl2 = girlOptional.orElse(new Girl("赵敏")); System.out.println(girl2); }
@Test public void test02(){ Boy boy = new Boy();//情况一:boy为空,girl为空 boy = null; String girl = getGirl(boy); System.out.println(girl); Boy boy1 = new Boy();//情况二:boy非空,girl为空 String girl1 = getGirl(boy1); System.out.println(girl1); Boy boy2 = new Boy(new Girl("赵敏")); String girl2 = getGirl(boy2); System.out.println(girl2); } public String getGirl(Boy boy){ Optional<Boy> optionalBoy = Optional.ofNullable(boy); //此时的boy一定非空 Boy boy1 = optionalBoy.orElse(new Boy(new Girl("周芷若"))); Girl girl = boy1.getGirl(); Optional<Girl> optionalGirl = Optional.ofNullable(girl); //此时的girl一定非空 Girl girl1 = optionalGirl.orElse(new Girl("小昭")); return girl1.getName(); }