简介:在学习了Java8的新特性后整理了Lambda表达式和StreamAPI的一些常用的方法与技巧,可以使我们的代码更简洁。

一、Lambda表达式

1.基本使用

特点:Lambda表达式是简洁地表示可传递的匿名函数的一种方式:它没有名称,但它 有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。

举个例子:当我们使用匿名内部类时需要重写方法

        //重写compare方法
		//比较单词的首字母大小
        Collections.sort(words,new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                    o1=o1.trim();
                    o2=o2.trim();
                    char c1 = (o1).charAt(0);
                    char c2 = (o2).charAt(0);
                   return c1-c2;
            }
        });

而Lambda表达式仅需要一行。

 //java8 Lambda表达式
        Collections.sort(words, (o1, o2) -> o1.charAt(0) - o2.charAt(0));

解释一下:

  • 参数列表——这里它采用了Comparator中compare方法的参数,两个String类型。

  • 箭头——箭头->把参数列表与Lambda主体分隔开。

  • Lambda主体——比较两个String的首字母大小。表达式就是Lambda的返回值了。

  • 花括号与圆括号——当一行代码解决不了问题时,可以使用花括号多行代码。

 //多行代码演示
 Runnable runnable = ()-> {
     System.out.println("lambda");
     System.out.println("lambda");
 };

代码实现:

public class LambdaTest {
    //基本使用
    @Test
    public void test(){
        //1、无参数
        new Runnable() {
            @Override
            public void run() {
                System.out.println("lambda");
            }
        };
        //等价于
        Runnable runnable = ()-> System.out.println("lambda");
   // *****************************************************************    
        //2、两个参数
        new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
        //等价于
        Comparator<Integer> comparator = (o1,o2)->Integer.compare(o1,o2);
 // *****************************************************************    
        //3、一个参数
        new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        //等价于下面的lambda语句
        Consumer<String> consumer1 = (String s) -> System.out.println(s);
  // *****************************************************************    
        //4、类型推断
        Consumer<String> consumer = (s) -> System.out.println(s);
        //5、一个参数可以以不需要括号
        consumer = s -> System.out.println(s);
    }
2.环绕执行模式

介绍函数式接口:只有一个抽象方法的接口

环绕执行(execute around)模式资源处理(例如处理文件或数据库)时一个常见的模式就是打开一个资源,做一些处理, 然后关闭资源。这个设置和清理阶段很像,并且会围绕着执行处理的那些重要代码。


//创建函数函数式接口
@FunctionalInterface
public interface BufferedReaderProcessor {
    String process(BufferedReader b) throws IOException;
}


@Test
public void test5() throws IOException {
     String twoLines = processFile(new BufferedReaderProcessor() {
            @Override
            public String process(BufferedReader b) throws IOException {
                return  b.readLine() + b.readLine();
            }
        });
       //等价于
     twoLines =processFile((BufferedReader br) -> br.readLine() + br.readLine());
}

public static String processFile(BufferedReaderProcessor p) throws
        IOException {
    try (BufferedReader br =
                 new BufferedReader(new FileReader("data.txt"))) {
        return p.process(br);
    }
}
3.四大函数式接口

在这里插入图片描述

//四大函数式接口
@Test
public void test1(){
    //消费型:对类型为T的对象应用操作,包含方法:void accept(T t)
    happyTime(500, new Consumer<Double>() {
        @Override
        public void accept(Double aDouble) {
            System.out.println("学习太累了,去干饭,花了"+aDouble);
        }
    });
    //等价于
    happyTime(400,money->  System.out.println("学习太累了,去干饭,花了"+money));
}
public void  happyTime(double money,Consumer<Double> consumer){
    consumer.accept(money);
}



@Test
public void test2(){
    //断定型:确定类型为T的对象是否满足某约束,并返回boolean值。包含方法:boolean test(T t)
    List<String> list = Arrays.asList("北京", "南京","天津" , "东京","西京", "普京");
    List<String> filterStr = filterString(list, new Predicate<String>() {
        @Override
        public boolean test(String s) {
            return s.contains("京");
        }
    });
    System.out.println(filterStr);
	 //等价于
    List<String> filterStr1 = filterString(list,s->s.contains("京"));

}
//根据某种规则过滤,规则有Predicate的方法决定
public List<String> filterString(List<String> list, Predicate<String> predicate){
    ArrayList<String> list1 = new ArrayList<>();
        for (String str:list){
            if (predicate.test(str))
                list1.add(str);
        }
    return list1;
}




@Test
public void test3(){
        //供给型:返回类型为T的对象,包含方法:T get()
        Supplier<Person> sup = new Supplier<Person>() {
            @Override
            public Person get() {
                return new Person("张三");
            }
        };
		//等价于
        sup = ()->new Person("王五");
        System.out.println(sup.get());

    }



@Test
   public void test4(){
          //函数型:对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t)
       Function<String,Person> fun =new Function<String, Person>() {
           @Override
           public Person apply(String s) {
               return new Person(s);
           }
       };
       //等价于
       fun = name->new Person(name);
   }

使用到的Person类

class Person{
    Integer id;
    String name;

    public Integer getId() {
        return id;
    }

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

    public Person(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(id, person.id) &&
                Objects.equals(name, person.name);
    }


    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}
4.方法引用

简介: 方法引用让你可以重复使用现有的方法定义,并像Lambda一样传递它们,本质上就是Lambda表达式 ,而Lambda表达式作为函数式接口的实例。 所以方法引用,也是函数式接口的实例。

使用格式(类(或对象)::方法名):

  1. 对象::非静态方法 (列如Person类的实例化对象p的getNeme()方法,写作p::getName)
  2. 类∶:静态方法 (例如Integer的parseInt方法,写作Integer::parseInt)。
  3. 类::非静态方法 (例如 String 的 length 方法,写作 String::length)

方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同!

方法引用的示例:

Lambda表达式等效的方法引用
(Person p) -> p.getName()p::getName
()->Thread.currentThread().dumpStack()Thread.currentThread()::dumpStack
(str, i) -> str.substring(i)String::substring
(String s) -> System.out.println(s)System.out::println

代码实现:

   @Test
   public void test3(){
//       对象::非静态方法
       Person person = new Person("张三");
       Supplier<String> stringSupplier = ()->person.getName();
       System.out.println("**************");
       Supplier<String> stringSupplier1 = person::getName;
       System.out.println(stringSupplier1.get());


//    类::静态方法
       Comparator<Integer> comparator = (t1,t2)->Integer.compare(t1,t2);
       System.out.println(comparator.compare(12,21));

       Comparator<Integer> comparator1 =Integer::compare;
       System.out.println(comparator.compare(12,21));


//       类::非静态方法
       Comparator<String> comparator2 = (s1,s2)->s1.compareTo(s2);
       Comparator<String> comparator3 =String::compareTo;
       System.out.println(comparator3.compare("sss", "ss"));
   }
5.构造函数引用

简介: 对于一个现有构造函数,可以利用它的名称和关键字new来创建它的一个引用: ClassName::new。它的功能与指向静态方法的引用类似

在这里插入图片描述

/**
 * @Auther: Parsifal
 * @Date: 2021/03/21/10:07
 * @Description:
 */
public class ConstructorRefTest {

    //构造器引用
    //Supplier中的T get()
    //Employee中的空参构造器:()
    @Test
    public void test4(){
        Supplier<Person> sup = new Supplier<Person>() {
            @Override
            public Person get() {
                return new Person("张三");
            }
        };
		//lambda表达式
        sup = ()->new Person("王五");
        System.out.println(sup.get());

        //方法引用
        sup = Person::new;

    }
    //Function中的 R apply(T t)
    @Test
    public void test5(){
        Function<String,Person> fun =new Function<String, Person>() {
            @Override
            public Person apply(String s) {
                return new Person(s);
            }
        };
	   //lambda表达式
       fun = name->new Person(name);
		//方法引用
        fun = Person::new;

    }

    //BiFunction中的R apply(T t,U u)
    @Test
    public void test6(){
        BiFunction<Integer,String,Person> bif = new BiFunction<Integer, String, Person>() {
            @Override
            public Person apply(Integer integer, String s) {
                return new Person(integer,s);
            }
        };
		//lambda表达式
        bif = (id,name)->new Person(id,name);
        //方法引用
        bif = Person::new;

    }

    //数组引用
    //Function中的R apply(T t)
    @Test
    public void test7(){
        Function<Integer,String[]> function =new Function<Integer, String[]>() {
            @Override
            public String[] apply(Integer integer) {
                return new String[integer];
            }
        };
        //lambda表达式
        function = length-> new String[length];
        String[] str = function.apply(5);
        System.out.println(str);

        //方法引用
        function = String[]::new;

}

二、StreamAPI

流的简介: 流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不 是临时编写一个实现)。就现在来说,你可以把它们看成遍历数据集的高级迭代器

流的好处:

  • 声明性——更简洁,更易读

  • 可复合——更灵活

  • 可并行——性能更好

注意: 请注意,和迭代器类似,流只能遍历一次。遍历完之后,这个流已经被使用掉了。 可以从原始数据源那里再获得一个新的流来重新遍历一遍,就像迭代器一样

1.创建流
//集合创建流
@Test
public  void test(){
    List<Person> persons = getPersons();
    //顺序流
    Stream<Person> stream = persons.stream();
    //并行流
    Stream<Person> personStream = persons.parallelStream();

}
//数组创建流
@Test
public  void test1(){
    int[] arr = new int[]{1,2,3,4,5,6};
    IntStream stream = Arrays.stream(arr);
}
//通过steam的of()
@Test
public  void test2(){
    Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
}
//无限流
@Test
public  void test3(){
    //迭代
    //遍历前十个偶数
    Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);
    //生成
    Stream.generate(Math::random).limit(10).forEach(System.out::println);

}
2.流的中间操作
(1)筛选与切片

在这里插入图片描述

代码实现:

//中间操作
	//切片与筛选
    @Test
    public  void test4(){
        List<Person> persons = getPersons();
	//    filter(Predicate p)——接收 Lambda ,从流中排除某些元素。
        persons.stream().filter(e->e.getId()>2000).forEach(System.out::println);
        System.out.println("*************");
	//    limit(n)——截断流,使其元素不超过给定数量。
        persons.stream().limit(1).forEach(System.out::println);
        System.out.println("*************");

	//    skip(n)——跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补
        persons.stream().skip(2).forEach(System.out::println);
        System.out.println("*************");
	//    distinct()——筛选,通过流所生成元素的hashCode() 和 equals()去除重复元素
        persons.add(new Person(1319,"斯诺克"));
        persons.stream().distinct().forEach(System.out::println);
}

//自定义排序
    @Test
    public  void test6(){
        List<Integer> list = Arrays.asList(12, 3232, 12, 3, 545, 2, 114, 5, 53);
        list.stream().sorted().forEach(System.out::println);
        //异常:没有实现comparable接口
	//        List<Person> people = getPersons();
	//        people.stream().sorted().forEach(System.out::println);
        List<Person> persons = getPersons();
        persons.stream().sorted((e1,e2)->Integer.compare(e1.id,e2.id)).forEach(System.out::println);
    }
  /**
     * 获得Person对象的数组
     * @return
     */
    public List<Person>  getPersons(){
        ArrayList<Person> people = new ArrayList<>();
        people.add(new Person(1119,"张三"));
        people.add(new Person(2439,"斯塔克"));
        people.add(new Person(1319,"斯诺克"));
        people.add(new Person(5421,"李四"));
        people.add(new Person(1432,"亚佛"));
        people.add(new Person(1123,"丫头"));
        people.add(new Person(4213,"莉莉"));
        people.add(new Person(1813,"王五"));
        return  people;
    }

(2)映射

简介:流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映 射成一个新的元素

在这里插入图片描述

代码实现:

//映射
@Test
public  void test5() {
    List<Person> persons = getPersons();
    //map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
    List<String> list = Arrays.asList("vv", "bb", "aa");
    list.stream().map(str->str.toUpperCase()).forEach(System.out::println);
    // 练习:获取员工姓名长度大于3的员工的姓名。
    List<String> names = persons.stream().map(Person::getName).filter(name -> name.length() > 2).collect(Collectors.toList());
    System.out.println(names);


    // flatMap(Function f)———接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
    Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest::fromStringToStream);
    streamStream.forEach(s->s.forEach(System.out::println));

    Stream<Character> characterStream = list.stream().flatMap(StreamAPITest::fromStringToStream);
    characterStream.forEach(System.out::println);

}

    /**
     * 把字符串转换成char型数组的流
     * @param str
     * @return
     */
    public static Stream<Character> fromStringToStream(String str){
        ArrayList<Character> characters = new ArrayList<>();
        for (Character c: str.toCharArray()){
            characters.add(c);
        }
        return characters.stream();
    }

    /**
     * 获得Person对象的数组
     * @return
     */
    public List<Person>  getPersons(){
        ArrayList<Person> people = new ArrayList<>();
        people.add(new Person(1119,"张三"));
        people.add(new Person(2439,"斯塔克"));
        people.add(new Person(1319,"斯诺克"));
        people.add(new Person(5421,"李四"));
        people.add(new Person(1432,"亚佛"));
        people.add(new Person(1123,"丫头"));
        people.add(new Person(4213,"莉莉"));
        people.add(new Person(1813,"王五"));
        return  people;
    }
3.终止操作

在这里插入图片描述

(1)匹配和查找

一个常见的数据处理套路是看看数据集中的某些元素是否匹配一个给定的属性。Stream API通过allMatch、anyMatch、noneMatch、findFirst和findAny方法提供了这样的工具。

 //终止操作
    @Test
    public  void test7(){
        List<Person> persons = getPersons();

//        allMatch(Predicate p)——检查是否匹配所有元素。
        boolean allMatch = persons.stream().allMatch(e -> e.getId() > 1100);
//        anyMatch(Predicate p)——检查是否至少匹配一个元素。
        boolean anyMatch = persons.stream().anyMatch(e -> e.getId() > 1100);
//        noneMatch(Predicate p)——检查是否没有匹配的元素。
        boolean noneMatch = persons.stream().noneMatch(e -> e.getId() > 1100);

//        findFirst——返回第一个元素
        Optional<Person> first = persons.stream().findFirst();
        System.out.println(first);
//        findAny———返回当前流中的任意元素count—返回流中元素的总个数
        Optional<Person> any = persons.parallelStream().findAny();
        System.out.println(any);
        //count - 返回流的元素总个数
        long count = persons.stream().filter(e -> e.getId() > 1000).count();
        System.out.println(count);
//        max (Comparator c)—一—返回流中最大值
        Optional<Integer> max = persons.stream().map(e -> e.getId()).max(Integer::compareTo);
        Optional<Person> max1 = persons.stream().max((e1, e2) -> Integer.compare(e1.getId(), e2.getId()));
        System.out.println(max1);
        System.out.println(max);
//        min(Comparator c)—返回流中最小值
        Optional<Person> min = persons.stream().min((e1, e2) -> Integer.compare(e1.getId(), e2.getId()));
        System.out.println(min);
//        forEach(consumer c)一内部迭代
    }
(2)规约

简介: 将流 中所有元素反复结合起来,得到一个值,比如一个Integer。这样的查询可以被归类为归约操作 (将流归约成一个值)。用函数式编程语言的术语来说,这称为折叠(fold)

//    reduce(T identity,BinaryOperator)-可以将流中元素反复结合起来.得到一个值返回T
//    reduce(BinaryOperator)..…可以将流中元素反复结合起来,得到一个值。返回 optional<Test
    //规约
   @Test
    public  void test8(){
        //计算1-10的值
       List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
       Integer reduce = list.stream().reduce(0, Integer::sum);
       System.out.println(reduce);
        //计算id总和
       List<Person> persons = getPersons();
       Optional<Integer> reduce1 = persons.stream().map(Person::getId).reduce(Integer::sum);
       System.out.println(reduce1.get());

   }
(3)收集的基本使用

在这里插入图片描述

在这里插入图片描述

  //收集
    @Test
   public  void test9(){
        //收集数据为List数组
       List<Person> persons = getPersons();
       List<Person> collect = persons.stream().filter(e -> e.getId() > 2000).collect(Collectors.toList());
       System.out.println(collect);


       //groupingBy收集数据为Map,分组展示
       Map<String, List<Person>> stringListMap = persons.stream().collect(groupingBy(person -> {
           if (person.getId() > 2000) return "id 大于 2000";
           else
               return "id 小于 2000";
       }));
       System.out.println(stringListMap);
       //map
//       {id 大于 2000=[Person{id=2439, name='斯塔克'}, Person{id=5421, name='李四'}, Person{id=4213, name='莉莉'}], 
//       id 小于 2000=[Person{id=1119, name='张三'}, Person{id=1319, name='斯诺克'}, Person{id=1432, name='亚佛'}, Person{id=1123, name='丫头'}, Person{id=1813, name='王五'}]}

   }

如果你认真看了代码就会发现,出现了一个 Optional类那么这个类是拿来干什么的呢?

(4)Optional类简介

Optional类(java.util.Optional)是一个容器类,代表一个值存在或不存在。在 上面的代码中,findAny可能什么元素都没找到。Java 8的库设计人员引入了Optional,这 样就不用返回众所周知容易出问题的null了

通俗的来讲,就是防止空指针异常

Optional类方法简介

  • Optional.of(T t) :创建一个Optional实例,t必须非空;
  • Optional.empty() : 创建一个空的Optional实例
  • Optional.ofNullabLe(T t): t可以为null

代码实现:

//女孩类
public class Girl {
    String name;

    public Girl() {
    }

    public Girl(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "girl{" +
                "name='" + name + '\'' +
                '}';
    }
}
//包含女孩的男孩类
public class Boy {
     Girl girl;

     public Boy() {
     }

     public Boy(Girl girl) {
          this.girl = girl;
     }

     @Override
     public String toString() {
          return "Boy{" +
                  "girl=" + girl +
                  '}';
     }

     public Girl getGirl() {
          return girl;
     }

     public void setGirl(Girl girl) {
          this.girl = girl;
     }
}
/**
 * @Auther: Parsifal
 * @Date: 2021/03/21/16:46
 * @Description: 
 */
public class OptionalTest {
    //利用of() 创建一个girl的Optional对象 
    @Test
    public void test(){
        Girl girl = new Girl();
        //of(T t) t非空
        Optional<Girl> optionalGirl = Optional.of(girl);

    }
    //利用ofNullable() 创建一个gril对象(可为空)
    @Test
    public void test1(){
        Girl girl = new Girl();
        girl=null;
        //可空
        Optional<Girl> optionalGirl = Optional.ofNullable(girl);
        System.out.println(optionalGirl);
//       l/orELse(T t1):如果单前的Optional内部封装的t是非空的,则返回内部的t.
//       如果内部的是空的,则返回orELse()方法中的参数t1.
        Girl girl1 = optionalGirl.orElse(new Girl("刘亦菲"));
        System.out.println(girl1);
    }
    
    //测试
    @Test
    public void test2(){
     Boy boy = new Boy(new Girl("小🐟")); //小🐟
     boy = new Boy();//戚薇
//     boy=null; //张沈
        String girlName = getGirlName(boy);
        System.out.println(girlName);
    }
    
     /**
     * 利用Optional类获得boy中girl的名字,防止空指针异常
     * @param boy
     * @return
     */
    public  String getGirlName(Boy boy){
        Optional<Boy> boy1 = Optional.ofNullable(boy);
        Boy boy2 = boy1.orElse(new Boy(new Girl("戚薇")));
        Girl girl = boy2.getGirl();
        Optional<Girl> girl1 = Optional.ofNullable(girl);
        Girl girl2 = girl1.orElse(new Girl("张沈"));

        return girl2.getName();
    }
}
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐