JAVA高级-java8的新特性与java8之上

14 篇文章 0 订阅
11 篇文章 1 订阅

java8新特性

Lambda表达式

  • Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API

  • Lambda表达式是一种语法规则,接口中的抽象方法的形参列表(左)-> 重写的抽象方法的方法体(右)=Lambda表达式

  • 类型 自定义参数=(目标/方法体类型)->(函数体)

  • 下面举一个很简单的例子。

    @Test
    public void test1(){
    
        Runnable runnable = new Runnable(){
            @Override
            public void run() {
                System.out.println("这是一个线程!");
            }
        };
        runnable.run();  
    }
    
    • 以上是Runnable接口,可见,run()便是抽象方法,那么可以根据上面的公式,写出Lambda表达式。

      Runnable runnable1=()-> System.out.println("Lambda表达式线程!");
      
      • 可见我们现在是可以省略new,Runnable(),和花括号的。
    • ()便是接口中抽象方法的形参列表,->为指向符,最后便是方法体。

  • 以上是一个无参的例子,来一个有参的例子。

        Comparator<Integer> objectComparator = new Comparator<Integer>(){
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        };
        System.out.println(objectComparator.compare(1, 2));
    
    }
    
    • 可见,这里的形参列表为o1,o2,方法体为return o1.compareTo(o2),那么可以根据公式等到Lambda表达式。

      Comparator<Integer> r=(o1,o2)->o1.compareTo(o2);
      System.out.println(r.compare(1,2));
      
      • 注意:当我们只有一个形参的时候是可以省略参数左右的括号的。
  • 然后,我们引入一个思考,既然Lambda表达式引入是为了方便我们写代码,那是否更加简洁呢?这里自然是可以的。

    • Comparator<Integer> a=Integer::compare;
      System.out.println(a.compare(1, 2));
      
      • 左边返回一个类型,右边是方法体,进行什么类型的比较。
  • 但我们仔细观察以上的代码就会发现,我没有提到当方法体里有多条语句的时候。

    • 我们可以将以上方法更改,让其更加合理介绍Lanbda表达式。

      Comparator<Integer> objectComparator = new Comparator<Integer>(){
          @Override
          public int compare(Integer o1, Integer o2) {
               System.out.println("多一条语句!");
               return o1.compareTo(o2);
          }
      };
      
    • 我们可以将这些代码用Lambda表达式

      Comparator<Integer> a=Integer::compare;
      System.out.println(a.compare(1, 2));
      //-1
      

      会发现它只返回了-1,也就是说,当我们里面有其他语句的时候,若是不显式表达我们另外的代码,就会默认是系统的

      所以,面对这种情况,我们Lambda只能写成下列各式。

      Comparator<Integer> r1=(o1,o2)->{
          System.out.println("多一条语句!");
          return o1.compareTo(o2);
      };
      System.out.println(r1.compare(1,2));
      

      我们会发现,不仅我们的return不可以省略,还必须带上我们的{}

  • 归结以上练习,我们可以明白一个道理,在Lambda表达式中,似乎在告诉我们一个道理:能省则省。

函数式(Functional))接口

  • 只包含一个抽象方法的接口,称为函数式接口,例如上面的Runnable接口。

  • 我们可以使用使用 @FunctionalInterface 注解,自定义函数式接口,但是不写注解也可以,写了注解方便我们检查是否是函数式接口。

  • 在Java8里提供了丰富的函数式接口

    • Consumer消费型接口 ,对类型为T的对象应用操作,包含方法:void accept(T t)

    • Supplier供给型接口 ,返回类型为T的对象,包含方法:T get()

    • Function<T, R>函数型接口,对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)

    • Predicate断定型接口,确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法:boolean test(T t)

    • 下面提供额外的,需要自取。

      https://i.loli.net/2021/06/20/JLhBtUWRlHsmk5N.png

  • Lambda表达式正是实现了这些函数式接口。所以Lambda表达式也是对象。

  • Consumer消费型接口

        @Test
        public void test1(){
    //        调用下面方法,我们肯定要在Consumer里重写accept方法
            TestConsumer(1, new Consumer<Integer>() {
                @Override
                public void accept(Integer integer) {
                    System.out.println(integer);
                }
            });
        }
    
        public void TestConsumer(int a, Consumer<Integer> consume){
            consume.accept(a);
        }
    
        @Test
        public void test1(){
            /*下面是Lambda表达式写法*/
            TestConsumer(1,integer -> System.out.println(integer));
        }
    
        public void TestConsumer(int a, Consumer<Integer> consume){
            consume.accept(a);
        }
    
    • 可以看见,实现同样的功能,用Lambda表达式精简很多,但增强了代码的阅读难度。
  • Supplier供给型接口

    /*Supplier<T>供给型接口*/
    @Test
    public void test2(){
        String s="a";
        getSupplier(s, new Supplier<String>() {
            @Override
            public String get() {
                return s;
            }
        });
    }
    
    public void getSupplier(String a, Supplier<String> s){
        System.out.println(s.get());//a
    }
    
    /*Supplier<T>供给型接口*/
    @Test
    public void test2(){
        String s="a";
        getSupplier(s,()->s);
    }
    
    public void getSupplier(String a, Supplier<String> s){
        System.out.println(s.get());//a
    }
    
  • Function<T, R>函数型接口

        /*Function<T, R>函数型接口*/
    
        @Test
        public void test3(){
            getFunction(new Function<Integer, String>() {
                @Override
                public String apply(Integer integer) {
                    System.out.println("这是一个字符串!");
                    return integer+"";
                }
            });
        }
    
        public void getFunction(Function<Integer,String> s){
    //        对Integer操纵
            System.out.println(s.apply(123));
    //        这是一个字符串!
    //        123
    //        这是一个字符串!
    //        123
        }
    
        /*Function<T, R>函数型接口*/
        @Test
        public void test3(){
            getFunction(integer -> {
                System.out.println("这是一个字符串!");
                return integer+"";
            } );
        }
    
        public void getFunction(Function<Integer,String> s){
    //        对Integer操纵
            System.out.println(s.apply(123));
    //        这是一个字符串!
    //        123
    //        这是一个字符串!
    //        123
        }
    
  • Predicate断定型接口

        /*Predicate<T>断定型接口*/
        @Test
        public void test4(){
            getPredicated(new Predicate<Integer>() {
                @Override
                public boolean test(Integer integer) {
                    return integer==123;
                }
            });
    
            getPredicated(integer -> integer==123);
        }
    
        public void getPredicated(Predicate<Integer> i){
            if (i.test(123)){
                System.out.println("这是123");
            }else System.out.println("这不是123");
    //        这是123
    //        这是123
        }
    
        /*Predicate<T>断定型接口*/
        @Test
        public void test4(){
            getPredicated(integer -> integer==123);
        }
    
        public void getPredicated(Predicate<Integer> i){
            if (i.test(123)){
                System.out.println("这是123");
            }else System.out.println("这不是123");
    //        这是123
    //        这是123
        }
    

方法引用

  • 在这之前,我们提到过有个Lambda表达式可以简化成

    Comparator<Integer> a=Integer::compare;
    System.out.println(a.compare(1, 2));
    //-1
    

    此为方法引用。

  • 要从上面的Lambda表达式去理解方法引用比较困难,我们可以总结出这个公式。

    类型 变量 = 类 (对象):: 方法名

    Comparator a=Integer::compare,我们可以理解一下这条语句,得到类型Comparator,变量为a,Integer为我们要比较的类型,方法名为compare。

    a.compare(1, 2),这条语句就是我们要得到变量a,用a调用compare方法,来比较(1,2);

    通俗理解为,方法引用只是一种引用,省去形参,但我们必须传入实参。

    这个类型必须是方法引用以上说明的接口实现类型

  • “=”后面主要是:

    对象::实例方法名(这里具体到了哪个对象的方法,自然可以,不能使用功能型接口)

    类::静态方法名(这里也自然可以,因为静态方法跟着类一样加载,也不能使用功能型接口)

    类::实例方法名(本质上类::实例方法是不行的,但是我们可以这样理解,这里表示一个大类下的大类方法,但是具体应用还是得应用到对象,使用功能型接口)

    注意:没有对象::静态方法名

package com.hyb.LambdaTest;

import org.junit.Test;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @program: FunctionalMethodTest
 * @description:
 * @author: Huang Yubin
 * @create: 2021-06-18 17:50
 **/

public class FunctionalMethodTest {
    @Test
    public void test1(){

//        通过对象去调用
        Person hyb = new Person("hyb",20);
        Supplier<String> s=hyb::getName;
        /*已经具体到哪个对象的方法了*/
        System.out.println(s.get());

    }

    @Test
    public void test2(){
//        通过类去调用静态
        Supplier<Integer> i=Person::getStaticAge;

        /*这里为什么可以用供给型接口?
        * 因为静态方法是随着类的加载而加载的,在调用的时候已经具现出来了。*/
        System.out.println(i.get());
    }

    @Test
    public void test3(){

        //通过类去调用
        Person hyb = new Person("hyb", 20);
        Function<Person,Integer> i=Person::getAge;

       /*这里很容易被误导,用供给型接口去声明i
       * 其实这是不行的,供给型是返回一个具体的值,而这个功能性接口,强调getAge这个功能,不代表哪个对象*/


        System.out.println(i.apply(hyb));

    }
}
  • 方法引用比较抽象,要注意要获取的变量类型使用哪个接口实现。

构造器引用

  • 与方法引用类似,对此还有数组引用,只要将它们看成两种特殊的类就可以了。
package com.hyb.LambdaTest;

import org.junit.Test;

import java.util.Arrays;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @program: FunctionalConstructionTest
 * @description:构造器引用
 * @author: Huang Yubin
 * @create: 2021-06-19 07:40
 **/

public class FunctionalConstructionTest {
//    这里我们还是以Person类为例,并加入了一个构造器以说明情况
//      public Person(double salary) {
//          this.salary = salary;
//      }
    @Test
    public void test1(){
//        Supplier<T>单个参数构造器
        Supplier<Person> salary=()->new Person(2000);
        System.out.println(salary.get());//Person{name='null', age=0, salary=2000.0}
        /*若是重写Supplier里的方法,只有一条语句,new Person,返回给salary*/

//        下面我们用构造器调用
        Supplier<Person> salary1=Person::new;
        System.out.println(salary1.get());
        /*但你会发现这好像不可以初始化对象的构造器!*/
    }

    /*上面既然提到了Supplier调用构造器无法初始化的问题,我们用Function试一试*/
    /*单个参数构造器*/
    @Test
    public void test2(){
        Function<Double,Person> r=salary->new Person(salary);//对Double的salary操作,返回一个Person类型的对象
        System.out.println(r.apply(2000.0));
//        Person{name='null', age=0, salary=2000.0}

//        构造器引用
        Function<Double,Person> r1=Person::new;
        System.out.println(r1.apply(2000.0));
//        Person{name='null', age=0, salary=2000.0}
        /*结构供给型接口构造器引用无法初始化问题*/
    }

    /*当两个参数初始化时,Java里提供了一个接口BiFunction<T, U, R>,*/
    @Test
    public void test3(){
        BiFunction<String ,Integer,Person> r=Person::new;
        System.out.println(r.apply("hyb", 20));
//        Person{name='hyb', age=20, salary=0.0}
        /*若是想要设置多个参数*/
        Person hyb = r.apply("hyb", 20);
        hyb.setSalary(2000);
        System.out.println(hyb);
//        Person{name='hyb', age=20, salary=2000.0}
    }

    /*数组引用*/
    @Test
    public void test4(){
        Supplier<String[]> r=()->new String[5];
        System.out.println(Arrays.toString(r.get()));

//        Supplier<String[]> New= String[]::new;//这样不行,因为供给型是返回一个具体数组
        System.out.println("----------------");

        Function<Integer,String[]> r1=length->new String[length];
        System.out.println(Arrays.toString(r1.apply(6)));

        Function<Integer,String[]> New=String[]::new;

        String[] apply = New.apply(5);
        System.out.println(Arrays.toString(apply));
//        [null, null, null, null, null]

    }
}

Stream API

  • Stream讲的是计算和CPU打交道,而集合讲究数据的存储,与内存打交道。

  • 关于Stream的三个特点:

    ①Stream 自己不会存储元素。

    ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。

    ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

  • Stream有三步操作流程

    1. 创建一个数据源,获取一个流。
    2. 中间操作,如映射等。
    3. 终止操作,一旦终止不再执行,每次执行完都会默认关闭

创建

  • 有四种方式,在这之前,我们先建立一个类
package com.hyb.StreamAPI;

/**
 * @program: Student
 * @description:
 * @author: Huang Yubin
 * @create: 2021-06-19 09:12
 **/

public class Student {
    private String name;
    private int age;
    private String id;
    private String sex;

    public Student() {
    }

    public Student(String name, int age, String id, String sex) {
        this.name = name;
        this.age = age;
        this.id = id;
        this.sex = sex;
    }

    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 String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id='" + id + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }

}
package com.hyb.StreamAPI;

import org.junit.Test;

import java.sql.SQLOutput;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @program: SteamAPITest
 * @description:
 * @author: Huang Yubin
 * @create: 2021-06-19 09:04
 **/

public class SteamAPITest {

    public static List<Student> getList(){
        ArrayList<Student> s = new ArrayList<>();
        s.add(new Student("hyb",20,"代号01","男"));
        s.add(new Student("hyb1",21,"代号02","男"));
        s.add(new Student("hyb2",22,"代号03","男"));
        s.add(new Student("hyb3",23,"代号04","男"));
        return s;
    }

    /*方式一,通过集合*/
    @Test
    public void test1(){

//         default Stream<E> stream() : 返回一个顺序流
        Stream<Student> studentStream1 = getList().stream();

//         default Stream<E> parallelStream() : 返回一个并行流
        Stream<Student> studentStream2 = getList().parallelStream();


    }

    /*方式二,通过数组*/

    @Test
    public void test2(){
//        Arrays中的static <T> Stream<T> stream(T[] array): 返回一个流
        int[] ints = {1, 2, 3, 4, 5, 6};
        IntStream stream1 = Arrays.stream(ints);

    }

    /*方式三:通过Stream的of()*/

    public void test3(){

        Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);

    }

    /*方式四:(了解)创建无限流*/

    @Test
    public void test4(){
//         迭代
//        public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
//        final UnaryOperator<T> f返回T
//         生成
//        public static<T> Stream<T> generate(Supplier<T> s)

        Stream.iterate(0,t->t+1).limit(10).forEach(System.out::println);//遍历0,1,2,3,4,5,6,7,8,9

        Stream.generate(Math::random).limit(10).forEach(System.out::println);//前十个随机数



    }



}

中间操作

  • 筛选与切片
  • 映射
  • 排序
  • 匹配与查找

筛选与切片

  • filter(Predicate p) 接收 Lambda , 从流中排除某些元素
  • distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
  • limit(long maxSize) 截断流,使其元素不超过给定数量
  • **skip(long n)**跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
package com.hyb.StreamAPI;

import com.hyb.LambdaTest.Person;
import org.junit.Test;

import java.sql.SQLOutput;
import java.util.ArrayList;
import java.util.List;

/**
 * @program: StreamMiddleOperation
 * @description:
 * @author: Huang Yubin
 * @create: 2021-06-19 09:39
 **/

public class StreamMiddleOperation {

    public static List<Student> getList(){
        ArrayList<Student> s = new ArrayList<>();
        s.add(new Student("hyb",20,"代号01","男"));
        s.add(new Student("hyb1",21,"代号02","男"));
        s.add(new Student("hyb2",22,"代号03","男"));
        s.add(new Student("hyb3",23,"代号04","男"));
        return s;
    }

//- **filter(Predicate p)** 接收 Lambda , 从流中排除某些元素,()里返回一个布尔值,表示是否存在某个值,便可以过滤掉某个值。
//- **distinct()** 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
//- **limit(long maxSize)** 截断流,使其元素不超过给定数量
//- **skip(long n)**跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
    @Test
    public void test1(){

        List<Student> list = getList();
        list.stream().filter(r->r.getName().equals("hyb")).forEach(System.out::println);
        //返回一群对象的getName方法,判断是否存在hyb,并forEach输出
        System.out.println("----------------------------------------");
        list.add(new Student("hyb",20,"代号01","男"));
        list.stream().distinct().forEach(System.out::println);
//        Student{name='hyb', age=20, id='代号01', sex='男'}
//        Student{name='hyb1', age=21, id='代号02', sex='男'}
//        Student{name='hyb2', age=22, id='代号03', sex='男'}
//        Student{name='hyb3', age=23, id='代号04', sex='男'}
        System.out.println("----------------------------123");
        list.forEach(System.out::println);
        System.out.println("——————————————————————————————————————————————————");
        list.stream().limit(1).forEach(System.out::println);
//        Student{name='hyb', age=20, id='代号01', sex='男'}
        System.out.println("____________________________________________");
        list.stream().skip(2).forEach(System.out::println);
        System.out.println("=====================");
        list.forEach(System.out::println);
//        Student{name='hyb', age=20, id='代号01', sex='男'}
//        Student{name='hyb1', age=21, id='代号02', sex='男'}
//        Student{name='hyb2', age=22, id='代号03', sex='男'}
//        Student{name='hyb3', age=23, id='代号04', sex='男'}
//        Student{name='hyb', age=20, id='代号01', sex='男'}

    }
}

映射

public static List<Student> getNewList(){
    ArrayList<Student> s = new ArrayList<>();
    s.add(new Student("hyb",20,"代号01","男"));
    s.add(new Student("hyb1",21,"代号02","男"));
    s.add(new Student("hyb2",22,"代号03","男"));
    s.add(new Student("hyb3",23,"代号04","男"));
    return s;
}
  • **map(Function f)**接收一个函数作为参数,该函数会被应用到你要映射的元素上,并将其映射成一个新的元素。

    • 映射容易搞混,里面一定要是功能函数,一般用Lambda表达式更简洁,而括号里的功能函数就决定了可以映射出的东西。
    • 集合.流(流化).映射(该流要映射的东西(功能)).遍历(次数由该集合决定)
        @Test
        public void test2(){
    //        找出学生姓名长度大于3的
    //        过滤(映射出所有名字())长度大于3
            List<Student> newList = getNewList();
            newList.stream().map(Student::getName).filter((name)->name.length()>3).forEach(System.out::println);
    //        集合.流.映射(所有的名字).过滤.(所有长度大于3的名字).遍历(打印所有值)
           
    //        具体细节说明,比如里面的Lambda表达式请看上一章,有详细说明
        }
    
  • **mapToDouble(ToDoubleFunction f)**接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。

  • **mapToInt(ToIntFunction f)**接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。

  • **mapToLong(ToLongFunction f)**接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。

  • **flatMap(Function f)**接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

    • 引出问题:若有二维映射出现,如何解决?解决无非的主要问题就是,遍历问题。

          @Test
          public void test() {
      
      //      我们先自定义了一个集合,里面放置String类型的元素
              List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
              
              /*那么问题来了,若是我们要将一个一个字符取出来,怎么做?*/
              
              /*答案自然不言而喻,再次打散,但你要看该元素还能不能打散。
              * 我们定义一个filterCharacter方法,去提供将《一个一个字符串》打散,
              * 然后转换成流,再去映射。*/
              
              
      //        得到这个流后,我们采取映射,记住这里的类型是《流中流(整个strList集合是一个,里面每一个字符串转换成的流也是一个)》
              Stream<Stream<Character>> stream2 = strList.stream()
                      .map(StreamMiddleOperation::filterCharacter);
      
      //        既然是流中流,肯定要进行两遍forEach
              stream2.forEach((sm) -> {
                  sm.forEach(System.out::println);
              });
      
              System.out.println("---------------------------------------------");
              
              
              /*那么有没有更加适合的办法去遍历这种流中流呢?提供了flatMap,其他步骤都一样,只是forEach少了一遍。*/
              
              Stream<Character> stream3 = strList.stream()
                      .flatMap(StreamMiddleOperation::filterCharacter);
              stream3.forEach(System.out::println);
          }
      
          public static Stream<Character> filterCharacter(String str) {
      //        声名一个集合去接收
              List<Character> list = new ArrayList<>();
      //        toCharArray是str转换成字符串数组
              for (Character ch : str.toCharArray()) {
                  list.add(ch);
              }
              count++;
              System.out.println(count);
      //        返回这个集合打散成的流
              return list.stream();
          }
      
      • 探讨:

        List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
        ....
         Stream<Character> stream3 = strList.stream()
                        .flatMap(StreamMiddleOperation::filterCharacter);
                stream3.forEach(System.out::println);
        ....
            public static Stream<Character> filterCharacter(String str) {
        //        声名一个集合去接收
                List<Character> list = new ArrayList<>();
        //        toCharArray是str转换成字符串数组
                for (Character ch : str.toCharArray()) {
                    list.add(ch);
                }
                System.out.println(count);
        //        返回这个集合打散成的流
                return list.stream();
            }
        

        映射的时候是将整个strList集合里的所有字符串一起映射,还是一个一个字符串映射输出呢?

        只要我们通过以下代码,便可以知道答案。

        private static int count = 0;//整个类下定义
        ......
        	Stream<Character> stream3 = strList.stream()
                        .flatMap(StreamMiddleOperation::filterCharacter);
                stream3.forEach(System.out::println);	
           	if (count != 0 && count != 1) {
                    System.out.println("该流每循环一次就被打散一次!");
            }
            public static Stream<Character> filterCharacter(String str) {
        //        声名一个集合去接收
                List<Character> list = new ArrayList<>();
        //        toCharArray是str转换成字符串数组
                for (Character ch : str.toCharArray()) {
                    list.add(ch);
                }
                count++;
                System.out.println(count);
        //        返回这个集合打散成的流
                return list.stream();
            }
        

        观察count的值,我们便会知道这个二级映射是每进行完一次字符串映射,才进行下一个字符串映射的。

    • 解决问题之后,我们再来探讨,若是二维集合里装的都是对象,该如何?下例子可看。

    • 大概意思是这样:若一个集合A里装了a个对象,集合B装了b个对象。在Java中,提供了addAll()方法,可以将两个结集合结合,总数为两集合元素的个数总和,但Java中还有一个方法add(),若A.add(B)的集合总数是a+1的,也就是将集合B当成了一个元素添加到了集合A中,那么此刻又该如何映射?

      小编思路知识有限,且这只是学习笔记,下面只给出探讨过程,答案是错误的

          public static List<Object> getNewList() {
              List<Object> s = new ArrayList<>();
              s.add(new Student("hyb", 20, "代号01", "男"));
              s.add(new Student("hyb1", 21, "代号02", "男"));
              /*s.add(new Student("hyb2",22,"代号03","男"));
              s.add(new Student("hyb3",23,"代号04","男"));*/
              return s;
          }
      
          @Test
          public void test2() {
      
              
              /*经过上面的例题,我们发现,map异常的强大,但你可能会想,
               * 如何集合是一个二维集合呢?我们该如何去多级映射呢?
               * 答案不言而喻,只要我们一层一层去映射,便可以得到结果。
               * 但要是遍历结果的话,map只能做到一层一层遍历。
               *
               * 那有没有一种方法可以做到直接遍历呢?
               *
               * 在Java中,提供了一个比map更强大的方法,flatMap,可以做到直接遍历*/
      
              /*下面用一个例子来练习这个方法,为了方便演示,我们不约束集合的类型*/
      
      
              List<Object> newList = getNewList();
              List<Object> newList1 = getNewList();
              newList1.add(new Student("hyb3", 23, "代号04", "男"));
              newList.add( newList1);
              System.out.println(newList);
              System.out.println();
      
              /*经过上面的变换,我们知道,集合newList里是镶嵌有集合newList1的,
              * 如此,我们不难发现,我们只是单纯地将集合newList一维映射出来
              * 会出现镶嵌集合的情况,如下:
              * Student{name='hyb', age=20, id='代号01', sex='男'}
                Student{name='hyb1', age=21, id='代号02', sex='男'}
      
                [Student{name='hyb', age=20, id='代号01', sex='男'},
                Student{name='hyb1', age=21, id='代号02', sex='男'},
                Student{name='hyb3', age=23, id='代号04', sex='男'}]
              *
              * 而我们的主要目的是要将镶嵌的集合也解脱,一行一行的输出,如下:
                Student{name='hyb', age=20, id='代号01', sex='男'}
                Student{name='hyb1', age=21, id='代号02', sex='男'}
                Student{name='hyb', age=20, id='代号01', sex='男'}
                Student{name='hyb1', age=21, id='代号02', sex='男'}
                Student{name='hyb3', age=23, id='代号04', sex='男'}
              * */
      
              /*经过以上的问题的发现,我们可以尝试下列解决方案*/
      //        newList.stream().flatMap(s->newList1.stream()).forEach(System.out::println);
              /*上述可以自己跑跑,会出现三次重复输出,且输出的是newList1集合的元素,
               * 这很容易解释,三次输出是因为newList集合长度就是3,而输出newList1是因为你要映射的就是这个集合
               * 这是初学者很容易走的坑,或者在flatMap的()里填入要遍历的集合本身。*/
      
      
              /*那么二维映射该怎么解决呢?
               * 现在我们面临的主要问题是:
               * 1.我们将一个集合打散成流,是将集合中的元素全部打散,而这里的元素有可能本来就是集合,
               * 我们要将他们全部打散,还要将元素打散。
               * */
              Stream<Stream<Object>> stream = newList.stream().map(StreamMiddleOperation::getList);
              stream.forEach(s->{
                  s.forEach(System.out::println);
              });
      
      
          }
      
          public static Stream<Object> getList(Object obj) {
              ArrayList<Object> objects = new ArrayList<>();
              objects.add(obj);
              return Arrays.stream(objects.toArray());
      
          }
      

排序

  • sorted() 产生一个新流,其中按自然顺序排序

  • sorted(Comparator com) 产生一个新流,其中按比较器顺序排序

package com.hyb.StreamAPI;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * @program: SortTest
 * @description:
 * @author: Huang Yubin
 * @create: 2021-06-20 07:59
 **/

public class SortTest {

    public static List<Student> getNewList() {
        List<Student> s = new ArrayList<>();
        s.add(new Student("hyb", 20, "代号01", "男"));
        s.add(new Student("hyb1", 21, "代号02", "男"));
        s.add(new Student("hyb2",22,"代号03","男"));
        s.add(new Student("hyb3",23,"代号04","男"));
        return s;
    }

//    - **sorted()** 产生一个新流,其中按自然顺序排序

    @Test
    public void SortedTest(){
        List<Student> list = getNewList();
        list.stream().map(Student::getAge).sorted().forEach(System.out::println);
//        20
//        21
//        22
//        23
        list.stream().map(Student::getName).sorted().forEach(System.out::println);
//        hyb
//        hyb1
//        hyb2
//        hyb3
/*注意:若涉及到对象排序,所在类应该重写应有的排序方法。*/
    }

//    - **sorted(Comparator com)** 产生一个新流,其中按比较器顺序排序
    @Test
    public void sortedComparatorTest(){
        List<Student> list = getNewList();
        list.stream().map(Student::getName).sorted((a,b)->a.compareTo(b)).forEach(System.out::println);//String::compareTO
    }
}

终止操作

匹配与查找

  • allMatch(Predicate p) 检查是否匹配所有元素

  • anyMatch(Predicate p) 检查是否至少匹配一个元素

  • noneMatch(Predicate p) 检查是否没有匹配所有元素

  • findFirst() 返回第一个元素

  • findAny() 返回当前流中的任意元素

package com.hyb.StreamAPI;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @program: FindTest
 * @description:
 * @author: Huang Yubin
 * @create: 2021-06-20 08:15
 **/

public class FindTest {
    public static List<Student> getNewList() {
        List<Student> s = new ArrayList<>();
        s.add(new Student("hyb", 20, "代号01", "男"));
        s.add(new Student("hyb1", 21, "代号02", "男"));
        s.add(new Student("hyb2",22,"代号03","男"));
        s.add(new Student("hyb3",23,"代号04","男"));
        return s;
    }

//- **allMatch(Predicate p)** 检查是否匹配所有元素
    @Test
    public void AllMatchTest(){

        List<Student> list = getNewList();
        boolean ishyb = list.stream().map(Student::getName).allMatch( s-> "hyb".equals("hyb"));

        System.out.println(ishyb);//true
        boolean ishyb1 = list.stream().map(Student::getName).allMatch( s->s.equals("hyb"));//true

        boolean ishyb2 = list.stream().allMatch(r->r.getAge()>20);//true

    }
//
//- **anyMatch**(**Predicate p**) 检查是否至少匹配一个元素
    @Test
    public void AnyMatchTest(){

        List<Student> list = getNewList();
        boolean ishyb = list.stream().map(Student::getName).anyMatch(s -> s.equals("hyb"));
        System.out.println(ishyb);

        boolean ishyb1 = list.stream().map(Student::getAge).anyMatch(s->s>20);
        System.out.println(ishyb1);//true

        boolean b = list.stream().anyMatch(s -> s.getAge() < 10);
        System.out.println(b);//false

    }

//
//- **noneMatch(Predicate p)** 检查是否没有匹配所有元素
    @Test
    public void noneMatchTest(){

        List<Student> list = getNewList();
        boolean b = list.stream().noneMatch(s -> s.getAge() > 100);
        System.out.println(b);//true


    }

//
//- **findFirst()** 返回第一个元素
    @Test
    public void findFist(){
        List<Student> newList = getNewList();
        Optional<Student> first = newList.stream().findFirst();//类型待会会讲到
        System.out.println(first);
//        Optional[Student{name='hyb', age=20, id='代号01', sex='男'}]
    }

//
//- **findAny()** 返回当前流中的任意元素

    @Test
    public void findAnyTest(){
        List<Student> list = getNewList();
        Optional<Student> any = list.stream().findAny();
        System.out.println(any);
//        Optional[Student{name='hyb', age=20, id='代号01', sex='男'}]
    }
}
  • count() 返回流中元素总数

  • max(Comparator c) 返回流中最大值

  • min(Comparator c) 返回流中最小值

  • **forEach(Consumer c)**内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)

//    count() 返回流中元素总数
    @Test
    public void countTest(){
        List<Student> li = getNewList();
        System.out.println(li.stream().count());//4
    }
//    max(Comparator c) 返回流中最大值
    @Test
    public void maxTest(){
        List<Student> list = getNewList();
        Optional<Integer> max = list.stream().map(Student::getAge).max(Integer::compare);//23
        System.out.println(max);
    }

//    min(Comparator c) 返回流中最小值
    
//    forEach(Consumer c)
//    内部迭代(使用 Collection 接口需要用户去做迭代,
//            称为外部迭代。相反,Stream API 使用内部迭
//            代——它帮你把迭代做了)

归约

  • reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T

  • reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional

//    - **reduce(T iden, BinaryOperator b)** 可以将流中元素反复结合起来,得到一个值。返回 T
    @Test
    public void ReduceTest(){
        List<Student> list = getNewList();
        System.out.println(list.stream().map(Student::getAge).reduce(1, Integer::sum));//86
        System.out.println();
        list.stream().filter(s->s.getAge()<22).forEach(System.out::println);
        System.out.println(list.stream().map(Student::getAge).filter(s -> s < 22).reduce(0, Integer::sum));
    }


//
//    - **reduce(BinaryOperator b)** 可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
    @Test
    public void ReduceTest1(){
        List<Student> list = getNewList();
        Optional<Integer> reduce = list.stream().map(Student::getAge).reduce((a, b) -> a + b);
        System.out.println(reduce);//86
    }

收集

  • **collect(Collector c)**将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
    @Test
    public void collectTest(){
        List<Student> list = getNewList();
        List<Integer> collectList = list.stream().map(Student::getAge).collect(Collectors.toList());
        System.out.println(collectList);
//        [20, 21, 22, 23]

    }

Optional类

  • 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。

    以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,

    Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代

    码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。

  • Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表

    这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不

    存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。

  • Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在

    则isPresent()方法会返回true,调用get()方法会返回该对象。

  • Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

  • 创建Optional类对象的方法:

    • Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
    • Optional.empty() : 创建一个空的 Optional 实例
    • Optional.ofNullable(T t):t可以为null
  • 判断Optional容器中是否包含对象:

    • boolean isPresent() : 判断是否包含对象
    • void ifPresent(Consumer<? super T> consumer) **:**如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。
  • 获取Optional容器的对象:

    • T get(): 如果调用对象包含值,返回该值,否则抛异常

    • **T orElse(T other):**如果有值则将其返回,否则返回指定的other对象。

    • T orElseGet(Supplier<? extends T> other) **:**如果有值则将其返回,否则返回由

      Supplier接口实现提供的对象。

    • T orElseThrow(Supplier<? extends X> exceptionSupplier) **:**如果有值则将其返

      回,否则抛出由Supplier接口实现提供的异常。

    @Test
    public void test1() {
        Boy b = new Boy("张三");
        Optional<Girl> opt = Optional.ofNullable(b.getGrilFriend());
// 如果女朋友存在就打印女朋友的信息
        opt.ifPresent(System.out::println);
    }
    @Test
    public void test2() {
        Boy b = new Boy("张三");
        Optional<Girl> opt = Optional.ofNullable(b.getGrilFriend());
// 如果有女朋友就返回他的女朋友,否则只能欣赏“嫦娥”了
        Girl girl = opt.orElse(new Girl("嫦娥"));
        System.out.println("他的女朋友是:" + girl.getName());
    }
    @Test
    public void test3(){
        Optional<Employee> opt = Optional.of(new Employee("张三", 8888));
//判断opt中员工对象是否满足条件,如果满足就保留,否则返回空
        Optional<Employee> emp = opt.filter(e -> e.getSalary()>10000);
        System.out.println(emp);
    }
    @Test
    public void test4(){
        Optional<Employee> opt = Optional.of(new Employee("张三", 8888));
//如果opt中员工对象不为空,就涨薪10%
        Optional<Employee> emp = opt.map(e ->
        {e.setSalary(e.getSalary()%1.1);return e;});
        System.out.println(emp);
    }

java8之上

  • Java版本迭代过快,企业一般用Java8特性
  • 下列之给出一些版本的网址

官方提供的新特性列表:https://docs.oracle.com/javase/9/whatsnew/toc.htm#JSNEW-GUID-C23AFD78-C777-460B-8ACE-58BE5EA681F6

或参考 Open JDKhttp://openjdk.java.net/projects/jdk9/

在线Oracle JDK 9 Documentationhttps://docs.oracle.com/javase/9/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值