StreamAPI - - Java8新特性
一、引入
-
Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
-
使用StreamAPI对集合数据进行操作,就类似于使用SQL执行的数据库查询。简言之:StreamAPI提供了一种高效且易于使用的处理数据的方式
-
实际开发中,项目中的多数数据都来在MySQL,Oracle等。但现在数据源可以更多,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理。
-
Stream和Collection的区别
① Collection:是一种静态的内存数据结构,主要面向内存,存储在内存中
② Stream:是有关计算的。面向CPU,通过CPU实现计算。
二、Stream到底是什么
-
Stream是数据渠道,用于操作数据源(集合、数组等)所产生的元素序列。集合讲数据、Stream讲计算。
-
特性
① Stream 自己不会存储元素
② Stream 不会改变源对象。相反,他们会返回一个持有结果的先Stream
③ Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。 -
操作三步骤
① 创建Stream
创建一个基于某一数据源(如:集合、数组···)的流
② 中间操作
一个中间操作链,对该数据源的数据进行处理
③ 终止操作(终端操作)
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用(即:如果再想使用,需要从新再创建Stream)。
-
功能总结:(核心)
创建基于该集合的Stream对象,通过该Stream对象的操作来实现对该集合中数据的筛查、映射、排序等操作,产生一个符合某种条件的新的数据源,最后按照所需要的格式或条件终止操作并输出,
三、创建Stream对象
根据不同的数据结构类型,及数据的有无选择对应的创建方式
-
方式一:通过集合
@Test // 方式一:通过集合,可以产生两种流,顺序流和并行流 public void testCollection() { List<Employee> em = EmployeeData.getEmployees(); // 1.default Stream<E> stream() 返回一个顺序流 Stream<Employee> stream1 = em.stream(); // 2.default Stream<E> parallelStream() 返回一个并行流 Stream<Employee> stream2 = em.parallelStream(); }
-
方式二:通过数组
@Test // 方式二:通过数组,因为数据结构是数组 public void testArrays() { int[] ints = new int[]{1, 2, 3, 4, 5, 6}; // 调用Arrays类的static <T> Stream<T> stream(T[] array)方法 IntStream stream1 = Arrays.stream(ints); Employee em1 = new Employee(101, "Tom"); Employee em2 = new Employee(102, "Jack"); Employee[] ems = new Employee[]{em1,em2}; Stream<Employee> stream2 = Arrays.stream(ems); }
-
方式三:通过Stream的of()方法
@Test // 方式三:通过Stream的of()方法,没有数据,现造数据 public void testStreamOf() { Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5); }
-
方式四:创建无限流
@Test // 方式四:创建无限流,没有数据,特殊的造数据方式 public void testIterate(){ /* * 1.迭代 public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) * UnaryOperator<T> f :该函数式接口中的方法是T apply (T t),参数与返回值类型 * 一样,和一元运算结构一样,所以写成了Lambda表达式 */ Stream.iterate(0,t -> t+2).limit(10).forEach(System.out::println); /* * 2.生成 public static<T> Stream<T> generate(Supplier<T> s) * Supplier<T> s 内的抽象方法是一个无参有返回值的方法结构,与Math类中的 * random()一致 */ Stream.generate(Math::random).limit(19).forEach(System.out::println); }
四、Stream的中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!这种在终止操作时一次性全部处理的情况,被称为“惰性求值”
-
筛查与切片
@Test public void test1() { // 1.获取数据结构 List<Employee> list1 = EmployeeData.getEmployees(); // 2.创建Stream对象 Stream<Employee> stream1 = list1.stream(); // 3.排除 不符合条件的元素 stream1.filter(e -> e.getSalary() > 7000).forEach(System.out::println); System.out.println("--------------------------"); // 4.截断 使元素不超过该数量 (每一个中间操作前都必须重新创建Stream对象,因为其是惰性求值) Stream<Employee> stream2 = list1.stream(); stream2.limit(3).forEach(System.out::println); System.out.println("--------------------------"); // 5.跳过 跳过前面几个元素 Stream<Employee> stream3 = list1.stream(); stream3.skip(3).forEach(System.out::println); System.out.println("--------------------------"); // 6.筛查 使用元素重写的hashCode和equals方法自动去重 Stream<Employee> stream4 = list1.stream(); stream4.distinct().forEach(System.out::println); }
-
映射
public class StreamTes t{ @Test public void test2() { // 1.获得集合 List<String> list1 = Arrays.asList("aa", "bb", "cc", "dd"); // 2.创建基于该集合的Stream对象 Stream<String> stream1 = list1.stream(); // 3.map映射,集合中的每一个元素会通过该函数的映射条件生成一个新的元素 stream1.map(String :: toUpperCase).forEach(System.out::println); System.out.println("-----------------------"); // 4.flatMap映射 Stream<String> stream2 = list1.stream(); stream2.flatMap(StreamTest::fromStringToStream).forEach(System.out::println); System.out.println("-----------------------"); /* * 5.用map写上述flatMap的功能,(StreamTest::fromStringToStream)返回的 * 是Stream<Stream<Character>>类型数据, * 所以在forEach方法中在对每一个Stream类型数据进行forEach操作 */ Stream<String> stream3 = list1.stream(); stream3.map(StreamTest::fromStringToStream).forEach( s -> s.forEach(System.out::println)); } // 将字符串的字符转换成对应的Stream实例的方法 public static Stream<Character> fromStringToStream(String str) { ArrayList<Character> list = new ArrayList<>(); for (Character c : str.toCharArray()) { list.add(c); } return list.stream(); } }
-
排序
@Test public void test3() { // 1.获取集合 List<Employee> list = EmployeeData.getEmployees(); // 2.创建基于该集合的Stream对象 Stream<Employee> stream1 = list.stream(); // 3.用Lambda表达式写Comparator接口中compare方法的结构 stream1.sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge()) ).forEach(System.out::println); System.out.println("---------------------------"); Stream<Employee> stream2 = list.stream(); stream2.sorted((e1,e2) -> { int value = Integer.compare(e1.getAge(),e2.getAge()); if (value != 0) { return value; }else { return Double.compare(e1.getSalary(),e2.getSalary()); } }).forEach(System.out::println); }
五、终止操作
终止操作会从流的流水线生成结果,其结果可以是任何不是流的值,例如List、Integer,甚至 void
-
匹配与查找
@Test public void test4() { List<Employee> list = EmployeeData.getEmployees(); Stream<Employee> stream1 = list.stream(); // 1.allMatch 判断所有元素是否都满足该条件 boolean allMatch = stream1.allMatch(e -> e.getAge() > 18); System.out.println(allMatch); // 2.anyMatch 判断是否有满足该条件的元素 boolean anyMatch = list.stream().anyMatch(e -> e.getSalary() > 10000); System.out.println(anyMatch); // 3.nonMatch 判断所有元组是否都不满足该条件 boolean noneMatch = list.stream().noneMatch(e -> e.getName().startsWith("雷")); System.out.println(noneMatch); // 4.findFirst 返回流中的第一个元素 Optional<Employee> first = list.stream().findFirst(); System.out.println(first); // 5.findAny 返回流中的任意一个元素 Optional<Employee> any = list.parallelStream().findAny(); System.out.println(any); // 6.count 返回流中的总个数 long count = list.stream().count(); System.out.println(count); // 7.max 返回流中最大值 Optional<Employee> max = list.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(max); // 8.min 返回流中最小值 Optional<Employee> min = list.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(min); // 9.forEach 内部迭代 list.stream().forEach(System.out::println); System.out.println("---------------------"); list.forEach(System.out::println); }
-
归约
map和reduce的连接通常称为map-reduce模式,因为Google用它来进行网络搜索而出名。
@Test public void test5() { List<Employee> list = EmployeeData.getEmployees(); // 用map映射出集合中的各元素的工资项,新生成一个Double型的流 Stream<Double> stream = list.stream().map(Employee :: getSalary); // 用reduce对map映射新生成的流进行归约 Optional<Double> reduce = stream.reduce(Double::sum); System.out.println(reduce); }
-
收集
Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)
@Test public void test6() { List<Employee> list = EmployeeData.getEmployees(); list.stream().filter(e -> e.getSalary() > 7000) .collect(Collectors.toList()).forEach(System.out::println); }