Java 8 是一个非常成功的版本,Java8 新增的Stream
,配合同版本出现的 Lambda
,给我们操作集合提供了极大的便利。
强大Stream API 的 为什么用强大呢? java8两大招牌 一个是函数式编程 Lambda 表达式,一个是Stream流
Stream API(java.util.stream)把真正的函数式编程风格引入到java中,这是目前为止对java类库最好的补充。因为Stream API可以极大的提供Java程序员的生产力,让程序员写出更高效率和干净,简洁的代码
Stream是Java8处理结合的关键抽象概念,他可以指定你希望对集合进行的操作,可以执行非常复杂的查找,过滤和映射数据等操作,使用StreamAPI对集合数据进行操作,就类似于使用SQL执行的数据库查询
。可以使用Stream API来并行执行操作,简言之,Stream API提供一种高效且易于使用的处理数据的方式
实际开发中 ,项目中多数据源来自于Mysql,Oracle 等,但现在使用数据源可以更多了,有MongoDB,redis等,而这些NoSql的数据就需要Java层面去处理
Stream和 Collection集合的区别
Collection 是一种静态内存数据结构的一个容器
Stream是有关计算的
前面是主要面向内存,存储在内存中后者是主要是面向CPU的,通过CPU实现计算
一个数据源(如集合,数组)获取一个流
一个中间操作链,对数据源进行处理
一旦执行终止操作,就执行中间操作链,并且产生结果,之后不在被使用
/** * @projectName: Java8 * @package: Stream * @className: StreamTest * @author: 冷环渊 doomwatcher * @description: * <h3> * 1.Stream 关注的是对数据的运算,与CPU打交道 * 集合关注的事数据的存储,与内存打交道 * * 2. * - Stream 自己不会存储元素 * - Stream 不会改变源对象 相反,他们会返回一个持有结果的新Stream * - Stream 操作是延时执行的,这意味的他们会等到需要结果的时候才执行 * * 3. Stream 执行操作 * Stream 的实例化 * 一系列的中间操作(过滤 映射 。。。。) * 终止操作 * * 4. 说明 * 4.1 一个中间操作链,对数据源进行处理 * 4.2 一旦执行终止操作,就执行中间操作链,并产生结果,之后,不会再被使用 * * </h3> * @date: 2021/12/12 1:37 * @version: 1.0 */
public class StreamTest {
//Stream 创建方式一:通过集合
@Test
public void test1() {
List<Employee> employees = EmployeeData.getEmployees();
/* * default Stream<E> stream() 返回一个程序流 * 这里我们用的是 Collection接口中就实现的方法 * default Stream<E> stream() { * return StreamSupport.stream(spliterator(), false); *} * */
Stream<Employee> stream = employees.stream();
// default Stream<E> parallelStream() 返回一个并行流
Stream<Employee> parallelStream = employees.parallelStream();
}
/** * @author 冷环渊 Doomwatcher * @context: 创建 Stream 的方式二 数组 * Java 8 中 Arrays 的静态方法 stream() 可以获取数组流 * IntStream stream = Arrays.stream(arr); static <T> Stream<T> stream(T[] array)返回一个流 * 重载形式: * 能够处理 对应基本类型的数组 * ① public Static intStream stream(int[] array) * ② public Static LongStraem stream(long[] array) * ③ public Static DoubleStraam stream(double[] array) * @date: 2021/12/12 12:38 * @param * @return: */
@Test
public void Test2() {
int[] arr = {1, 2, 3, 4, 5, 6};
IntStream stream = Arrays.stream(arr);
Employee e1 = new Employee(1001, "tom");
Employee e2 = new Employee(1002, "Treey");
Employee[] employees = {e1, e2};
Stream<Employee> employeeStream = Arrays.stream(employees);
}
/** * @author 冷环渊 Doomwatcher * @context: * 创建 Stream三 : Stream本身的 of() 我们需要创建数据的时候 * @date: 2021/12/12 12:45 * @param * @return: void */
@Test
public void Test3() {
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
}
/** * @author 冷环渊 Doomwatcher * @context: * 创建方式四 创建无限流 * 我们需要批量的产生数据 * @date: 2021/12/12 12:47 * @param * @return: void */
@Test
public void Test4() {
/* 迭代 * public static<T> Stream<T> iterate(final T sead,final UnaryOperator) * 遍历前十个偶数 * 这里 参数 UNaryOperator继承自 function 也就是说我们最终调用的方法 是重写的apply * @FunctionalInterface * public interface UnaryOperator<T> extends Function<T, T> { * static <T> UnaryOperator<T> identity() { * return t -> t; * } * } * */
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).forEach(System.out::println);
}
}
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止 操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全 部处理,称为“惰性求值”。
//filter(Predicate p) 接收 Lambda , 从流中排除某些元素
@Test
public void test1() {
List<Employee> employeeList = EmployeeData.getEmployees();
Stream<Employee> stream = employeeList.stream();
//小练习,查询员工表中薪资大于7000的员工
System.out.println("filter-------------------");
stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
/*limit(long maxSize) 截断流,使其元素不超过给定数量 * 这里我们用 stream 会抛出异常,所以我们直接调用构造方法,每次都会生成新的流 * */
System.out.println("limit-------------------");
employeeList.stream().limit(3).forEach(System.out::println);
/* skip(long n) * 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 * */
System.out.println("skip--------------------");
employeeList.stream().skip(3).forEach(System.out::println);
//distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去 除重复元素
System.out.println("distinct----------------");
employeeList.add(new Employee(1010, "刘强东", 40, 8000));
employeeList.add(new Employee(1010, "刘强东", 40, 8000));
employeeList.add(new Employee(1010, "刘强东", 40, 8000));
employeeList.stream().distinct().forEach(System.out::println);
}
/** * @author 冷环渊 Doomwatcher * @context: 映射 * @date: 2021/12/12 14:15 * @param * @return: void */
@Test
public void test2() {
/* map(Function f) * 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 * 映射结果: * aa --> AA * bb --> BB * cc --> CC * dd --> DD * */
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
System.out.println();
List<Employee> employeeList = EmployeeData.getEmployees();
employeeList.stream().map(Employee::getName).filter(name -> name.length() > 3).forEach(System.out::println);
System.out.println();
/*flatMap(Function f) * 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流、*/
//这里是没有用 flatmap的版本 ,我们需要foreach之后再foreach才可以拆分到所有的元素
Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest2::fromStringToStream);
streamStream.forEach(s -> s.forEach(System.out::print));
System.out.println();
//使用了 flatmap
Stream<Character> stream = list.stream().flatMap(StreamAPITest2::fromStringToStream);
stream.forEach(System.out::print);
}
/** * @author 冷环渊 Doomwatcher * @context: * 将字符串中的多个字符构成的集合,转换成对应的Stream的实例 * @date: 2021/12/12 14:44 * @param str * @return: java.util.stream.Stream<java.lang.Character> */
public static Stream<Character> fromStringToStream(String str) {
ArrayList<Character> list = new ArrayList<>();
for (Character c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
/** * @author 冷环渊 Doomwatcher * @context: * =演示 list里放list * 【1,2,3,【4,5,6】】 * 打散开了再放入list * 【1,2,3,4,5,6】 * @date: 2021/12/12 14:33 * @param * @return: void */
@Test
public void test3() {
ArrayList list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
ArrayList list1 = new ArrayList();
list1.add(4);
list1.add(5);
list1.add(6);
list.add(list1);
System.out.println(list);
}
/** * @author 冷环渊 Doomwatcher * @context: 排序 * @date: 2021/12/12 16:42 * @param * @return: void */
@Test
public void tset4() {
//sorted() 产生一个新流,其中按自然顺序排序
List<Integer> list = Arrays.asList(12, 43, 65, 0, -1, 7);
list.stream().sorted().forEach(System.out::println);
/* 这里会出现问题 * 为什么? * 原因:Employee没有实现Comparble接口、 * List<Employee> employees = EmployeeData.getEmployees(); * employees.stream().sorted().forEach(System.out::println); * */
//sorted(Comparator com) 产生一个新流,其中按比较器顺序排序
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().sorted((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge())).forEach(System.out::println);
System.out.println();
//小练习,如果年龄一样就按收入排序
employees.add(new Employee(1011, "冷环渊", 34, 8000));
employees.stream().sorted((e1, e2) -> {
int agevalue = Integer.compare(e1.getAge(), e2.getAge());
if (agevalue != 0) {
return agevalue;
} else {
return -Double.compare(e1.getSalary(), e2.getSalary());
}
}).forEach(System.out::println);
}
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例 如:List、Integer,甚至是 void 。
流进行了终止操作后,不能再次使用。
allMatch(Predicate p) 检查是否匹配所有元素
anyMatch(Predicate p) 检查是否至少匹配一个元素
noneMatch(Predicate p) 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素总数
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代, 称为外部迭代。相反,Stream API 使用内部迭 代——它帮你把迭代做了)
/** * @author 冷环渊 Doomwatcher * @context: 匹配与查找 * @date: 2021/12/12 17:11 * @param * @return: void */
@Test
public void test1() {
//- allMatch(Predicate p) 检查是否匹配所有元素
List<Employee> employees = EmployeeData.getEmployees();
boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println("allMatch(Predicate p) 检查是否匹配所有元素: " + allMatch);
//- anyMatch(Predicate p) 检查是否至少匹配一个元素
boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
System.out.println(" anyMatch(Predicate p) 检查是否至少匹配一个元素: " + anyMatch);
//- noneMatch(Predicate p) 检查是否没有匹配所有元素
boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
System.out.println("noneMatch(Predicate p) 检查是否没有匹配所有元素: " + noneMatch);
//- findFirst() 返回第一个元素
Optional<Employee> first = employees.stream().findFirst();
System.out.println("findFirst() 返回第一个元素: " + first);
//- findAny() 返回当前流中的任意元素
Optional<Employee> any = employees.parallelStream().findAny();
System.out.println("findFirst() 返回第一个元素: " + any);
//- count() 返回流中元素总数
Long salary = employees.stream().filter(e -> e.getSalary() > 5000).count();
System.out.println("count() 返回流中元素总数: " + salary);
}
/** * @author 冷环渊 Doomwatcher * @context: * @date: 2021/12/12 23:30 * @param * @return: void */
@Test
public void test2() {
//- max(Comparator c) 返回流中最大值
List<Employee> employees = EmployeeData.getEmployees();
Stream<Double> stream = employees.stream().map(e -> e.getSalary());
Optional<Double> max = stream.max(Double::compare);
System.out.println("返回流中最大值:" + max);
//- min(Comparator c) 返回流中最小值
Optional<Employee> min = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println("返回流中最小值: " + min);
//- forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代, 称为外部迭代。相反,Stream API 使用内部迭 代——它帮你把迭代做了)
employees.stream().forEach(System.out::println);
//使用集合的方式
employees.forEach(System.out::println);
}
map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
/** * @author 冷环渊 Doomwatcher * @context: 规约 * @date: 2021/12/12 23:56 * @param * @return: void */
@Test
public void tset3() {
//- reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一 个值。返回 T
// 小练习 计算1-10的自然数总和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer resultReduce = list.stream().reduce(0, Integer::sum);
System.out.println("resultReduce: " + resultReduce);
//- reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一 个值。返回 Optional
// 小练习 计算公司所有员工的工资总和
List<Employee> employees = EmployeeData.getEmployees();
Optional<Double> slaryStream = employees.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(slaryStream);
}
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、 Map)。 另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,常用案例如下
/** * @author 冷环渊 Doomwatcher * @context: 收集 * @date: 2021/12/13 0:23 * @param * @return: */
@Test
public void test4() {
/* collet(Collector c) 将流转换成其他的形式,接收一个 Collector接口实现,用于给Stream中元素做汇总方法 * 练习一 查找工资大于6000的员工 返回一个 list 或者set * */
List<Employee> employees = EmployeeData.getEmployees();
//大于六千的list
List<Employee> employeeList = employees.stream().filter(employee -> employee.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out::println);
System.out.println();
//小于六千的set
Set<Employee> employeeSet = employees.stream().filter(employee -> employee.getSalary() < 6000).collect(Collectors.toSet());
employeeSet.forEach(System.out::println);
}
}
到这里我们已经对Stream的日常使用有了一些了解,可以看到功能和我们的数据库sql语句有相似之处,可以在代码层面处理数据。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/doomwatcher/article/details/121901670
内容来源于网络,如有侵权,请联系作者删除!