只要有人的地方,世界就不会是冰冷的,我们可以平凡,但绝对不可以平庸。
—————— 《平凡的世界》
人活着,就得随时准备经受磨难。他已经看过一些书,知道不论是普通人还是了不起的人,
都要在自己的一生中经历许多磨难。磨难使人坚强。
—————— 《平凡的世界》
hellip... 人哪,活着是这么的苦,一旦你从幸福的彼岸被抛到苦难的此岸,你真
是处处走投无路,而现在你才知道,在天堂与地狱之间原来也只有一步之遥。
——————《平凡的世界》
@toc
;另外一个则为是我们这个主题了:Stream API 了。
java.util.stream
包下的,Stream 是把真正的函数式编程 风格引入到 Java 中,这时目前为止对 java 类库最好的补充了,因为 Stream API 可以极大的提供 Java程序员的生产力,让程序员写出更高效率,干净,简洁的代码。在实际开发种,项目中多数据源都是来自于 MySQ,Oracle 等数据库的,但现在数据源可以更多了,有 MongDB,Radis 等,而这些 NoSQL 的数据就需要 Java层面去处理。
是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列,“集合讲的是数据,Stream 讲的是计算” 。
注意:
一个数据源(如:集合,数组),获取一个流
2.中间操作
一个中间操作链,对数据源的数据进行处理。
3. 终止操作(终端操作)
一旦执行终止操作,就执行中间操作链,并产生结果,之后,不会再被使用(也不可再使用)。
Stream 和以前的Collection操作不同, Stream操作还有两个基础的特征:
因为 Stream 是一个接口,所以我们无法通过 new 的方式创建该对象。
Java8 中的 Collection 接口被扩展,提供了两个获取流 的方法:
package blogs.blog13;
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.List;
import java.util.stream.Stream;
public class StreamAPITest {
public static void main(String[] args) {
List<Employee> employeeList = EmployeeData.getEmployees();
//default Stream<E> stream(): 返回一个顺序流
Stream<Employee> stream = employeeList.stream();
System.out.println(stream);
// default Stream<E> parallelStream : 返回一个并行流
Stream<Employee> employeeStream = employeeList.parallelStream();
System.out.println(employeeStream);
}
}
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
重载形式,能够处理对应基本类型的数组:
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class StreamAPITest {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5,6};
// 调用 Arrays 类中的 static<T> stream(T[] array)返回一个对象
IntStream stream = Arrays.stream(arr);
System.out.println(stream);
}
}
可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
import java.util.stream.Stream;
public class StreamAPITest {
public static void main(String[] args) {
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
System.out.println(stream);
}
}
可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
public static Stream iterate(final T seed, final UnaryOperator f)
public static Stream generate(Supplier s)
import java.util.stream.Stream;
public class StreamAPITest {
public static void main(String[] args) {
// 迭代:
//public static<T> Stream<T> inerate(final T seed,final UnaryOperator<T> f)
// 遍历前 10 个偶数
Stream.iterate(0,t->t+2).forEach(System.out::println);
// 生成:
// public static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
多个中间操作可以连接起来形成一个 流水线 ,除非流水线上触发终止操作,否则中间操作不会执行任何的处理! ,而终止操作时一次性全处理。 这样的 称为 惰性求值 。
如下是关于 Stream 中间操作筛选与切片的一些常用的方法
package blogs.blog13;
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.List;
import java.util.stream.Stream;
public class StreamAPITest02 {
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
// 创建一个 Stream 对象
Stream<Employee> stream = list.stream();
// 使用 filter 进行一个筛选
// boolean test(T t)
// 筛选出:工资大于 7000 的 Employee 对象
stream.filter(e->e.getSalary() > 7000).forEach(System.out::println);
}
}
注意: stream 和集合中的迭代器是一样的,不可多次不同结构的使用。 再次使用时需要新建一个 stream 对象,才能使用。简单的来说:就是 stream 对象一次只能对应一次操作,再进行一个新的操作时,必须新建一个 stream 对象才行。不然报java.lang.IllegalStateException
异常。
注意: 使用该方法,因为涉及到筛选,需要对元素数据进行一个比较判断,所以和集合同理:我们必须重写其元素的 hashCode() 和 equals() 方法,不然报异常;
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StreamAPITest02 {
public static void main(String[] args) {
List<Employee> list = new ArrayList<Employee>();
// distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
// 所以对应存储的 Employee 对象需要重写 hashCode() 和 equals()方法
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",20,8000));
list.stream().distinct().forEach(System.out::println);
}
}
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.List;
import java.util.stream.Stream;
public class StreamAPITest02 {
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
// 创建一个 Stream 对象
Stream<Employee> stream = list.stream();
// limit(n) 截断流: 筛选出 list 集合中存储的前3 个信息
stream.limit(3).forEach(System.out::println);
}
}
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class StreamAPITest02 {
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
// skip(n) 跳过元素,返回一个扔掉了前 n 个元素的流,若流中元素不足 n 个,则返回一个 空
// stream 不可二次使用,需要新建
Stream<Employee> skip = list.stream().skip(3);
skip.forEach(System.out::println); // 该操作是终止操作,并运用了方法引用
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamAPITest02 {
public static void main(String[] args) {
List<String> list = Arrays.asList("aa","bb","cc");
// 创建一个 Stream 对象
Stream<String> stream = list.stream();
Stream<String> stringStream = stream.map(s -> s.toUpperCase()); // toUpperCase() 将字母转换为大写的
stringStream.forEach(System.out::println); // Stream 终止操作
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamAPITest02 {
// 将字符串的多个字符构造的从集合转换为单个字符串并存储到 List 集合中
public static Stream<Character> fromStringToStream(String str) {
ArrayList<Character> list = new ArrayList<>();
for (Character character : str.toCharArray()) { // toCharArray()将一个字符串拆分为单个字符
list.add(character);
}
return list.stream();
}
public static void main(String[] args) {
// flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的
// 数据组成一个数据
List<String> list = Arrays.asList("aa","AA","bb");
Stream<String> stream = list.stream();
Stream<Character> characterStream = stream.flatMap(StreamAPITest02::fromStringToStream); // 方法引用
characterStream.forEach(System.out::println);
}
}
举例: 练习:获取员工姓名长度大于 3 的员工的姓名:
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamAPITest02 {
public static void main(String[] args) {
List<Employee> employees = EmployeeData.getEmployees();
// 创建 stream 对象
Stream<Employee> stream = employees.stream();
// 获取到一个关于 Employee 对象中的 有关 name 属性的 Stream 对象
Stream<String> stringStream = stream.map(e -> e.getName());
// 获取到该 Stream 对象中 name 长度大于 3 的名字
Stream<String> stringFilter = stringStream.filter(e -> e.length() > 3);
stringFilter.forEach(System.out::println);
}
}
注意:这里的排序要排序的元素信息,必须实现 Comparable 接口或者是 Comparator 定制排序 ,不然报异常,关于这部分排序内容,想要多加了解的,可以移步至:🔜🔜🔜 比较器: Comparable 与 Comparator 区别_ChinaRainbowSea的博客-CSDN博客
举例:soreted() 运用自然排序 ,排序的元素实现了 comparable 接口
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamAPITest02 {
public static void main(String[] args) {
// sorted -- 自然排序
// 注意排序:需要实现 Comparable 接口
// 注意泛型不能放基本数据类型
List<Integer> list = Arrays.asList(12,56,3,2,1);
// 创建 Stream 对象
Stream<Integer> stream = list.stream();
stream.forEach(System.out::println);
}
}
举例: sorted(comparator com ) 定制排序 通过年龄排序,默认是升序的 > 0 返回 1
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StreamAPITest02 {
public static void main(String[] args) {
// sorted(comparator com ) 定制排序 通过年龄排序,默认是升序的 > 0 返回 1,
List<Employee> employees = EmployeeData.getEmployees();
// 创建 Stream 对象
Stream<Employee> stream = employees.stream();
Stream<Employee> sorted = stream.sorted((e1, e2) -> {
int compare = Integer.compare(e1.getAge(), e2.getAge()); // 年龄排序
if (compare != 0) {
return compare;
} else { // 年龄一致,再通过比较器进一步排序,比较薪资排序
return Double.compare(e1.getSalary(), e2.getSalary());
}
});
sorted.forEach(System.out::println);
}
}
终端操作会从流水线 生成的结果。其结果可以是任何不是流的值,例如:list,Integer,甚至是 void
注意: Stream 流一旦执行了终止操作后,就不能再使用了。
boolean allMatch(Predicate<? super T> predicate); // 检查是否匹配所有元素
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.List;
public class StreamAPITest03 {
/**
* allMatch(Predicate p) 检查是否匹配所有元素。
* 练习:是否所有的员工的年龄都大于 18
*/
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
boolean b = list.stream().anyMatch(e -> e.getAge() > 18);
System.out.println(b);
}
}
boolean anyMatch(Predicate<? super T> predicate);
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.List;
public class StreamAPITest03 {
/**
* anyMath(Predicate p) 检查是否至少匹配一个元素:
* 练习: 是否存在元素的工资大于 10000
*/
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
boolean b = list.stream().anyMatch(e -> e.getSalary() > 1000);
System.out.println(b);
}
}
boolean noneMatch(Predicate<? super T> predicate);
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.List;
import java.util.stream.Stream;
public class StreamAPITest03 {
/**
* noneMatch(Predicate p) 检查是否没有匹配的元素。
* 练习: 是否存在员工姓 "雷"
*/
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
// String 中的startsWith 表示该字符串中是否含有该字符内容,有返回 true,没有返回 fasle
boolean b = stream.noneMatch(e -> e.getName().startsWith("雷"));
System.out.println(b);
}
}
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamAPITest03 {
/**
* findFirst 返回第一个元素
*/
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
Optional<Employee> first = stream.findFirst();
System.out.println(first);
}
}
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamAPITest03 {
/**
* findAny 返回当前流中的任意元素
*/
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
Optional<Employee> any = stream.findAny();
System.out.println(any);
}
}
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamAPITest03 {
/**
* count 返回流中元素的总个数
*/
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
long count = stream.count();
System.out.println(count);
}
}
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamAPITest03 {
/**
* max(Comparator c) 返回流中最大值
* 练习返回最高的工资。
*/
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
// 创建一个有关于 Employee 对象的 属性为salary的 Stream 流
Stream<Double> doubleSalary = stream.map(e -> e.getSalary());
Optional<Double> max = doubleSalary.max(Double::compareTo);// 方法引用
System.out.println(max);
}
}
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamAPITest03 {
/**
* max(Comparator c) 返回流中最大值
* 练习返回最低的工资。
*/
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
Stream<Employee> stream = list.stream();
// 创建一个有关于 Employee 对象的 属性为salary的 Stream 流
Stream<Double> doubleSalary = stream.map(e -> e.getSalary());
Optional<Double> min = doubleSalary.min(Double::compareTo);// 方法引用
System.out.println(min);
}
}
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamAPITest03 {
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
list.stream().forEach(System.out::println);
System.out.println("****************************");
// 集合的遍历操作
list.forEach(System.out::println);
}
}
如下是关于归约常用方法:
Optional<T> reduce(BinaryOperator<T> accumulator); //
T reduce(T identity, BinaryOperator<T> accumulator); //
补充: map 和 reduce 的连接通常为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
举例:
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamAPITest03 {
/**
* reduce(T identity, BinaryOperator ) 可以将流中元素反复结合起来,得到一个值。返回
* 练习: 计算 1-10 的自然数的和
*/
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5);
Stream<Integer> stream = list.stream();
Integer reduce = stream.reduce(0, Integer::sum);
System.out.println(reduce);
}
}
举例:
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamAPITest03 {
/**
* reduce(BinaryOperator) 可以将流中元素反复的结合起来,得到一个值,返回Optional<T>
* 练习: 计算公司所有员工的工资的总和
*/
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
// 获取到 Employees 员工工资的 Stream 流对象
Stream<Double> streamSalary = list.stream().map(e -> e.getSalary());
Optional<Double> reduce = streamSalary.reduce((d1, d2) -> d1 + d2);
System.out.println(reduce);
}
}
Collect(Collectior c) : 将流转换为其他形式,接收一个 Collector 接口的实现,用于 Stream 中元素做汇总的方法。
<R,A> R collect(Collector<? super T,A,R> collector);
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List ,Set,Map)。另外,Collectior 实用类提供了很多静态方法,可以方便创建常见收集器实例。具体方法与实例如下表:
import day33.java.Employee;
import day33.java.EmployeeData;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamAPITest03 {
/**
* collect(Collector c) 将流转换为其他形式,接收一个Collector 接口的实现,
* 用于给 Stream 中
* 练习1 查找工资大于 6000 的员工,结果返回为一个 List 或 set
*/
public static void main(String[] args) {
List<Employee> list = EmployeeData.getEmployees();
// 获取到一个工资大于 6000 的 Stream 流
Stream<Employee> employeeStream = list.stream().filter(e->e.getSalary() > 6000);
List<Employee> collect = employeeStream.collect(Collectors.toList());
collect.forEach(System.out::println);
}
}
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代 码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
Optional提供很多有用的方法,这样我们就不用显式进行空值检测
创建Optional类对象的方法:
import java.util.Optional;
public class OptionalTest {
public static void main(String[] args) {
Optional<Girl> optional = Optional.empty();
System.out.println(optional);
}
}
import java.util.Optional;
public class OptionalTest {
public static void main(String[] args) {
Girl girl = new Girl();
girl = null;
Optional<Girl> optional = Optional.ofNullable(girl); // ofNullable 中的 t 参数可以为空
System.out.println(optional);
}
}
判断Optional容器中是否包含对象:
获取Optional容器的对象:
import java.util.Optional;
public class OptionalTest {
// orElse(T t) 如果单前的 Optional 内部封装的t是非空的,则返回内部的 t,
// 如果内部的 t是空的,则返回orElse()方法中的参数t1.
// 使用 Optional 类的 getGirName()
public static String getGirName2(Girl girl) {
Optional<Girl> optional = Optional.ofNullable(girl);
// 如果 Optional 中的 girl 为 null ,则使用 如下的 new Girl(new Boy("肖战")) 替换就不为空了
// 不为 null 是不会发生替换的,使用原来的就可以了。
Girl girl2 = optional.orElse(new Girl(new Boy("肖战")));
Boy boy = girl2.getBoy();
/*Optional<Boy> boyOptional = Optional.ofNullable(boy);
Boy boy1 = boyOptional.orElse(new Boy("王一博"));*/
return boy.getName();
}
public static void main(String[] args) {
Girl girl = null;
String girName2 = getGirName2(girl);
System.out.println(girName2);
Girl girl2 = new Girl(new Boy("王一博"));
String girName = getGirName2(girl2);
System.out.println(girName);
}
}
java.lang.IllegalStateException
异常。限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后会有期 !!!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。