> 文档中心 > Java8新特性笔记--跟着楠哥学java--Stream Api

Java8新特性笔记--跟着楠哥学java--Stream Api


1.集合处理数据的弊端

当我在需要对集合中的元素进行操作的时候,除了必须的添加删除获取外,最典型的操作就是集合遍历

public class Demo01StreamApi {    public static void main(String[] args) { // 定义一个集合 List<String> list = Arrays.asList("张三","张三丰","成龙","周星驰"); // 1.获取所有姓张的信息 List<String> list1 = new ArrayList<>(); for (String s : list) {     if (s.startsWith("张")){  list1.add(s);     } } // 2.获取名称长度为3的用户 List<String> list2 = new ArrayList<>(); for (String s : list1) {     if (s.length() == 3){  list2.add(s);     } } // 3.输出所有用户信息 张姓且三个字 for (String s: list2) {     System.out.println(s); }    }}

上面的代码针对于我们不同的需求总是一字字的循环循环,这是我们希望有更加高效的处理方式,
我们可以通过jdk8中提供的Stream Api来解决这个问题。

Stream更加优雅的解决方案:

public class Demo02StreamApi {    public static void main(String[] args) { // 定义一个集合 List<String> list = Arrays.asList("张三","张三丰","成龙","周星驰"); // 1.获取所有姓张的信息 // 2.获取名称长度为3的用户 // 3.输出所有用户信息 张姓且三个字 list.stream()  .filter(s -> s.startsWith("张"))  .filter(s -> s.length() == 3)  .forEach(s -> System.out.println(s)); System.out.println("------------------------------------------------"); list.stream()  .filter(s -> s.startsWith("张"))  .filter(s -> s.length() == 3)  .forEach(System.out::println);    }}

上面的StreamAPI代码的含义:获取流,过滤张,过滤字长,逐一打印。代码相比于上面的案例更加简洁直观。

2.Stream流的作用

Stream API能让我们快速完成许多复杂的操作,如筛选,切片,映射,查找,去重,统计,匹配和归约。

3.Stream流的获取方式

3.1 根据Collection获取

首先,java.util.Collection接口中加入了default方法stream,也就是说Collection接口下的所有实现都可以
通过stream方法来获取Stream流对象

public class Demo03StreamApi {    public static void main(String[] args) { List<String> list = new ArrayList<>(); list.stream(); Set<String> set = new HashSet<>(); set.stream(); Vector vector = new Vector(); vector.stream();    }}

但是Map接口没有实现Collection接口,这时该怎么办呢? 这是可以根据Map获取对应的key value的集合。

public class Demo04StreamApi {    public static void main(String[] args) { Map<String, Object> map = new HashMap<>(); Stream<String> stream1 = map.keySet().stream(); Stream<Object> stream2 = map.values().stream(); Stream<Map.Entry<String, Object>> stream = map.entrySet().stream();    }}

3.2 通过Stream的of方法

在实际开发中我们不可避免的还是会操作到数组中的数据,由于数组对象不可能添加默认方法,所以Stream接口中
提供了静态方法of

public class Demo05StreamApi {    public static void main(String[] args) { Stream<String> a1 = Stream.of("a1", "a2", "a3", "a4"); String[] arr1 = {"aa","b","cc"}; Stream<String> arr11 = Stream.of(arr1); Integer[] arr2 = {1,2,3,4}; Stream<Integer> arr21 = Stream.of(arr2); arr21.forEach(System.out::println); // 注:基本数据类型的数组是不行的  会将数组数据看成一个整体 int[] arr3 = {1,2,3,4}; Stream.of(arr3).forEach(System.out::println);    }}

4.Stream常用方法介绍

终结方法:返回值类型不再是Sream类型的方法,不再支持链式调用,包括count和foreach。
非终结方法:返回值类型仍然是Stream类型的方法,支持链式调用。
Stream注意事项
1.Stream只能操作一次
2.Stream方法返回的是一个新的流
3.Stream不调用终结方法,中间的操作不会执行,必须以终结方法结束

4.1 forEach

foreach用来遍历流中的数据的

void forEach(Consumer<? super T> action);

该方法接受一个Consumer接口,会将每一个流元素交给函数处理

public class Demo07StreamApiForeach {    public static void main(String[] args) {Stream.of("a1", "a2", "a3")  .forEach(System.out::println);    }}

4.2 count

Stream流中的count方法用来统计其中的元素个数的

long count();

该方法返回一个long值,代表元素个数。

public class Demo08StreamApiCount {    public static void main(String[] args) { long count = Stream.of("a1", "a2", "a3").count(); System.out.println(count);    }}

4.3 filter

filter方法的作用是用来过滤数据的,返回符合条件的数据
可以通过filter方法讲一个流转换成另一个子集流

Stream<T> filter(Predicate<? super T> predicate);

该接口接受一个Predicate函数式接口参数作为筛选条件

public class Demo09StreamApiFilter {    public static void main(String[] args) { Stream.of("a1", "a2", "a3","bb","cc","dd").filter(s -> s.contains("a"))  .forEach(System.out::println);    }}

4.4 limit

limit方法可以对流进行截取处理,支取前n个数据

Stream<T> limit(long maxSize);

参数是一个long类型的数值,如果集合长度大于参数就进行截取,否则不操作:

public class Demo10StreamApiLimit {  public static void main(String[] args) {    Stream.of("a1", "a2", "a3", "bb", "cc", "aa", "dd").limit(5)     .forEach(System.out::println);  }}

4.5 skip

如果希望跳过前面几个元素,可以使用skip方法获取一个截取之后的新流:

public class Demo11StreamApiSkip {    public static void main(String[] args) { Stream.of("a1", "a2", "a3","bb","cc","aa","dd").skip(3)  .forEach(System.out::println);    }} 

4.6 map

如果我们需要将流中的元素映射到另一个流中可以使用map方法;

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的数据

public class Demo12StreamApiMap {    public static void main(String[] args) { Stream.of("1", "2", "3","4","5","6","7")  //.map(s -> Integer.parseInt(s))  .map(Integer::parseInt)  .forEach(System.out::println);    }}

4.7 sorted

如果需要将数据排序,可以使用sorted方法:

Stream<T> sorted();

在使用的时候可以根据自然规则排序,也可以通过比较来指定对应的排序规则

public class Demo13StreamApiSorted {  public static void main(String[] args) {    Stream.of("1", "9", "3","4","22","6","7")     .map(Integer::parseInt)     //.sorted() //根据数据的自然顺序排序     .sorted((o1,o2)->o2-o1) //根据比较指定排序规则     //.sorted(Integer::compareTo)     .forEach(System.out::println);  }}

4.8 distinct

如果要去掉重复的数据,可以使用distinct方法:

Stream<T> distinct();
public class Dmo14StreamApiDistinct {  public static void main(String[] args) {    Stream.of("1", "9", "3","4","3","6","7")     .distinct() //去掉重复数据     .forEach(System.out::println);    System.out.println("---------------------------------");    Stream.of(      new Person("张三",18,198),      new Person("李四",22,199),      new Person("张三",18,167)     ).distinct()     .forEach(System.out::println);  }}

Stream流中的 方法对于基本数据类型是可以直接去重的,但是对于自定义类型,我们是需要重写 hashCode和equals方法来移除重复元素

4.9 match

`如果需要判断数据是否匹配指定的条件,可以使用match相关的方法

boolean anyMatch(Predicate<? super T> predicate);// 元素是否有任意一个满足条件boolean allMatch(Predicate<? super T> predicate);// 元素是否都满足条件boolean noneMatch(Predicate<? super T> predicate);// 元素是否都不满足条件

使用

public class Demo15StreamApiMatch {    public static void main(String[] args) { boolean b = Stream.of("1", "9", "3", "4", "3", "6", "7")  .map(Integer::parseInt)  //.allMatch(s -> s > 0)  //.anyMatch(s -> s > 4)  .noneMatch(s-> s > 4); System.out.println(b);    }}

注意:match也是终结方法

4.10 find

如果我们需要找到某些元素,可以使用find方法来实现

Optional<T> findFirst();Optional<T> findAny();

使用

public class Demo16StreamApiFind {    public static void main(String[] args) { Optional<String> first = Stream.of("1", "9", "3", "4", "3", "6", "7").findFirst(); System.out.println(first.get()); Optional<String> any = Stream.of("1", "9", "3", "4", "3", "6", "7").findAny(); System.out.println(any.get());    }}

4.11 max和min

如果我们想要获取最大值和最小值没那么可以使用max和min方法

Optional<T> max(Comparator<? super T> comparator);Optional<T> min(Comparator<? super T> comparator);

使用

public class Demo17StreamApiMaxMin {    public static void main(String[] args) { Optional<Integer> first = Stream.of("1", "9", "3", "4", "3", "6", "7")  .map(Integer::parseInt)  .max((o1,o2)->o1-o2); System.out.println(first.get()); Optional<Integer> any = Stream.of("1", "9", "3", "4", "3", "6", "7")   .map(Integer::parseInt)   .min(((o1, o2) -> o1-o2)); System.out.println(any.get());    }}

4.12 reduce

如果需要将所有数据归纳得到一个数据,使用reduce方法

T reduce(T identity, BinaryOperator<T> accumulator);

使用

public class Demo18StreamApiReduce {    public static void main(String[] args) { Integer total = Stream.of(4, 5, 3, 9)  // identity是默认值  // 第一次的时候会将默认值赋给x  // 之后每次会将上一次的操作结果赋值给x y就是每次从数据中获取的元素  .reduce(0, (x, y) -> {      System.out.println("x="+x+",y="+y);      return x + y;  }); System.out.println(total); // 获取最大值 Integer max = Stream.of(4, 5, 3, 9)  .reduce(0, (x, y) -> {      return x > y ? x : y;  }); System.out.println(max); // 获取最小值 Optional<Integer> reduce2 = Stream.of(4, 5, 3, 9)  .reduce((x, y) -> {      return x < y ? x : y;  }); System.out.println(reduce2.get());    }}

4.13 map和reduce的组合

在实际开发中,我们经常会将map和reduce组合使用

public class Demo19StreamApiMapReduce {    public static void main(String[] args) { //求年龄总和 Integer sumAge = Stream.of(   new Person("张三", 18, 198),   new Person("李四", 22, 199),   new Person("张三", 18, 167),   new Person("赵六", 19, 176),   new Person("张三", 33, 180)  ).map(Person::getAge)  .reduce(0, Integer::sum); System.out.println(sumAge); // 求出年龄最大值 Integer maxAge = Stream.of(   new Person("张三", 18, 198),   new Person("李四", 22, 199),   new Person("张三", 18, 167),   new Person("赵六", 19, 176),   new Person("张三", 33, 180)  ).map(Person::getAge)  .reduce(0, Math::max); System.out.println(maxAge); //统计 字符 a 出现的次数 Integer count = Stream.of("a", "b", "c", "d", "a", "c", "a")  .map(s -> "a".equals(s) ? 1 : 0)  .reduce(0, Integer::sum); System.out.println(count);    }}

4.14 mapToInt

如果需要将Stream中的Integer类型转换成int类型,可以使用mapToInt方法来实现

public class Demo20StreamApiMapToInt {    public static void main(String[] args) { // Integer占用的内存比int多很多,在stream流操作中会自动拆装箱操作 Integer arr[] = {1,2,3,4,5}; Stream.of(arr)  .filter(integer -> integer > 0)  .forEach(System.out::println); // 为了提高程序代码的效率, 我们可以现将流中Integer数据转换为int数据,然后操作 Integer arr2[] = {1,2,3,4,5,7,9}; Stream.of(arr2)  .mapToInt(Integer::intValue)  .filter(i -> i > 4)  .forEach(System.out::println);    }}

4.15 concat

如果有两个流希望合并成为一个流,那么可以使用Stream接口的静态方法concat

public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) { Objects.requireNonNull(a); Objects.requireNonNull(b); @SuppressWarnings("unchecked") Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(  (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator()); Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel()); return stream.onClose(Streams.composedClose(a, b));    }

使用:

public class Demo21StreamApiConcat {    public static void main(String[] args) { Stream<String> stream1 = Stream.of("a", "b", "c"); Stream<String> stream2 = Stream.of("x", "y", "z"); // 通过concat方法将两个流合并成一个新的流 Stream.concat(stream1,stream2)  .forEach(System.out::println);    }}

4.16 综合案例

   public class Demo22StreamApiTest {  /**   * @methodName main   * @description   * 定义两个集合,然后再集合中存储多个用户名称,然后完成如下操作:   *   1.第一个队伍只保留姓名长度为3的成员   *   2.第一个队伍筛选后只要前三个   *   3.第二个队伍只要姓张的成员   *   4.第二个队伍筛选之后不要前两个人   *   5.将两个队伍合并成一个队伍   *   6.根据姓名创建Person对象   *   7.打印整个队伍的Person信息   * @author FredHe   * @param args   * @return   * @since 2022/4/23 18:09   */  public static void main(String[] args) {    List<String> list1 = Arrays.asList("迪丽热巴","宋远桥","苏星河","老子","庄子","孙子","洪七公");    List<String> list2 = Arrays.asList("古力娜扎","张无忌","张三丰","赵丽颖","张二狗","张天爱","张三");    // 1.第一个队伍只保留姓名长度为3的成员    // 2.第一个队伍筛选后只要前三个    Stream<String> stream1 = list1.stream().filter(s -> s.length() == 3).limit(3);    // 3.第二个队伍只要姓张的成员    // 4.第二个队伍筛选之后不要前两个人    Stream<String> stream2 = list2.stream().filter(s -> s.startsWith("张")).skip(2);    // 5.将两个队伍合并成一个队伍    Stream<String> stream3 = Stream.concat(stream1, stream2);    // 6.根据姓名创建Person对象    // 7.打印整个队伍的Person信息    stream3.map(Person::new).forEach(System.out::println);  }}

输出结果

Person(name=宋远桥, age=null, height=null)Person(name=苏星河, age=null, height=null)Person(name=洪七公, age=null, height=null)Person(name=张二狗, age=null, height=null)Person(name=张天爱, age=null, height=null)Person(name=张三, age=null, height=null)

未完待续

本人代码笔记gitee地址:https://gitee.com/FredHeYuTong/learn-java8