Java8新特性

Lambda

一、Lambda 表达式的基础语法:Java8中引入了一个新的操作符 “->” 该操作符称为箭头操作符或 Lambda 操作符,箭头操作符将 Lambda 表达式拆成两部分:

  • 左侧:Lambda 表达式的参数列表

  • 右侧:Lambda 表达式中多虚执行的功能,即 Lambda 体

语法格式一:无参数,无返回值

() -> System.out.println("Hello Lambda!");

语法格式二:有一个参数,并且无返回值

(x) -> System.out.println(x);

语法格式三:若只有一个参数,小括号可以省略不写

x -> System.out.println(x);

语法格式四:有两个以上的参数,有返回值,并且 Lambda 体重有多条语句.必须带上{ }(大括号),和 return

Comparator<Integer> com = (x,y) -> {
 	System.out.println("函数式接口");
    return Integer.compare(x,y);
};

语法格式五:若 Lambda 体中只有一条语句,return 和大括号都可以省略不写

Comparator<Integer> com = (x,y) -> Integer.compare(x,y);

语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即"类型推断"

(Integer x, Integer y) -> Integer.compare(x,y);
//可以省略 Integer
(x,y) -> Integer.compare(x,y);

左右遇一括号省,左侧推断类型省,能省则省

二、Lambda 表达式需要“函数式接口”的支持

函数式接口:接口中只有一个抽象的方法接口,称为函数式接口。可以使用注解@FunctionalInterface 修饰,可以检查是否是函数式接口。

方法引用:双冒号 ( :: )

英文(double colon),双冒号( : : ) 运算符,在Java8中被当做 方法的引用 ,方法的引用是与lambda表达式相关的一个重要的特性。它提供了一种不执行方法的方法。为此,方法的引用需要兼容的函数接口组成的目标类型上下文。说人话:就是在使用lambda表达式会创建匿名的方法,但有时候需要使用一个lambda表达式只调用一个已经存在的方法(不做其他)

Java 8 方法引用的一些语法:

1、静态方法引用(static method) 语法: classname::methodname 例如:Person::getAge

注意:Lambda 体中调用的方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。

@Test
public void test3(){
    //Lambda形式
    Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
    //方法引用
    Comparator<Integer> com1 = Integer::compare;
}

2、对象的实例方法引用语法:instancename::methodname 例如:System.out::println

@Test
public void test1(){
    PrintStream ps1 = System.out;
    //Lambda 引用
    Consumer<String> con = (x) -> ps1.println(x);
    
    PrintStream ps = System.out;
    Comsumer<String> con1 = ps::println;
    
    //方法引用
    Consumer<String> con2 = System.out::println;
    con2.accept("asasdas");
}

@Test
public void test2(){
    Employee emp = new Employee();
    //Lambda引用
    Supplier<String> sup = () -> emp.getName();
    String str = sup.get();
    System.out.println(str);
    
    //方法引用
    Supplier<Integer> sup2 = emp::getAge;
    Integer num = sup2.get();
    System.out.println(num);
}

3、对象的类方法引用语法: ClassName::methodname

若 Lambda 参数列表中的第一列参数是 实例方法的调用者,二第二个参数是实例方法的参数是,可以使用 ClassName :: methodName

@Test
public void test4(){
    //Lambda 方式
    BiPredicate<String,String> bp = (x,y) -> x.equals(y);
    
    //方法引用
    BiPredicate<String,String> bp = String::equals;
}

4、类构造器的引用语法:classname::new

注意:需要调用的构造器的参数列表要函数式接口中的抽象方法的参数列表保持一致!

@Test
public void test5(){
    Supplier<Employee> sup = () -> new Employee();
    
    //构造器引用方式
    Supplier<Employee> sup2 = Employee::new;
    Employee emp = sup2.get();
    System.out.println(emp);
}

5、数组构造器的引用方法:typename[]:new 例如:String[] :: new

@Test
public void test7(){
    //Lambda 形式
    Function<Integer,String[]> fun = (x) -> new String[x];
    String[] strs = fun.apply(10);
    System.out.println(strs.length);
   
    //方法引用
    Function<Integer,String[]> fun2 = String[]::new;
    String[] str2 = fun2.apply(20);
    System.out.println(str2.length);
}

Stream

第一步:创建 Stream

1、可以通过 Collection 系列集合提供的 stream() 或 parallelStream()

List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();

2、通过 Arrays 中的静态方法 stream() 获取数组流

Employee[] emps = new Employee[10];
Stream<Employee> stream2 = Arrays.stream(emps);

3、通过 Stream 类的静态方法 of()

Stream<Employee> stream3 = Stream.of("aa","bb","cc");

4、创建无限流

  • 迭代
Stream<Integer> stream4 = Stream.iterate(0,(x) -> x+2);
stream4.limit(10).forEach(System.out::println);
  • 生成
Stream.generate(() -> Math.random())
    .limit(5)
    .forEach(System.out::println);

第二步:中间操作

1、筛选与切片

  • IntStream filter(IntPredicate predicate); :接收 Lambda,从流中过滤出某些元素。
  • intStream limit(long maxSize): 返回由该流的元素组成的maxSize长度的流,截断长度不能超过maxSize。如果说maxSize为负数的话,那么他就会报 IllegalArgumrntException
  • IntStream skip(long n): 丢弃流的前n个元素后,返回由该流的其余元素组成的流。如果此流包含的元素小于n的话,则会返回一个空流,这是一个有状态的中间操作。
  • IntStream distinct(); : 不能加参数,返回该流的不同的元素组成的流(看下图),对于有序流,选择不同的元素是稳定的==(对于重复元素,首先在遇到顺序中出现的元素被保留,是通过所生成元素的hashCode() 和 equals()去重)== ,而对于无序流不能保证稳定性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w6VDig8Q-1642479011619)(Java8%E6%96%B0%E7%89%B9%E6%80%A7.assets/image-20220105151153318.png)]

2、映射

  • IntStream map(IntUnaryOperator mapper); : 接收Lambda表达式,将元素换成其他形式或提取信息。接收一个函数作为参数,该函数被应用到每个元素上,并将其映射成一个新元素。
@Test
public void test5(){
    List<String> list = Arrays.asList("aaa","bbb","ccc","ddd");

    list.Stream()
        .map((str) -> str.toUpperCase())
        .forEach(System.out::println);
}
  • IntStream flatMap(IntFunction<? extends IntStream> mapper);: 接收一个函数作为参数,将流中的每一个值都换成另一个流,然后把所有的流连接成一个流。

     String[] words = new String[]{"Hello","World"};
            List<String> a = Arrays.stream(words)
                    .map(word -> word.split(""))
                    .flatMap(Arrays::stream)
                    .distinct()
                    .collect(toList());
            a.forEach(System.out::print);
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XtC64pVV-1642479011620)(Java8%E6%96%B0%E7%89%B9%E6%80%A7.assets/70.png)]

3、排序

  • IntStream sorted(Comparator<? super T> comparator):返回该流的元素组成的排序后的流,排序的方法是Comparator提供的方法排序,对于有序的流排序稳定,对于无序的流不能保证稳定性。
    • 自然排序 sorted()
    • 定制排序 sorted(Comparator com)
@Test
public void test(){
    employee.stream()
        .sorted((o1,o2) -> {
            if(o1.getAge()==o2.getAge()){
                return e1.getName.compareTo(e2.getName());
            }else{
                return e1.getAge().compareTo(e2.getAge());
            }
        }).forEach(System.out::println);
}

4、查找与匹配

  • allMatch :检查是否匹配所有元素
  • anyMatch:检查是否至少匹配一个元素
  • noneMatch:检查是否没有匹配的元素
  • findFirst(): 返回流的第一个元素
  • OptionalInt findAny();: 返回描述流的某些某些元素,可以自由选择流中的任何元素。
  • OptionalInt max();:返回该流中的最大元素,如果说这个流是空的那么就返回一个空的可选项。是一个终端操作。
//相当于
return reduce(Integer::max)
  • OptionalInt min();:返回该流重元素的最小值,如果这个流是空的那么就的返回一个空的可选项。是一个终端操作
//相当于
return reduce(Integer::min);
  • long count(): 这是一个归约操作,返回该流中的元素个数。这是一个终端操作。
//相当于
return mapToLong(e -> 1L).sum();

5、归约

  • int reduce(int identity, IntBinaryOperator op);:使用提供的标示值和关联累加函数对该流的元素执行规约,并返回规约后的值。适用于求 sum(),min(),max(),average()
Sum, min, max, and average are all special cases of reduction. Summing a stream of numbers can be expressed as:
     int sum = integers.reduce(0, (a, b) -> a+b);
or more compactly:
     int sum = integers.reduce(0, Integer::sum);

6、收集

  • collect(Collector<? super T,A,R> collector): 将这个流的元素的各个分散的元素收集成一个流。将流转换成为其他形式。接受一个 Collectors 接口实现,用于Stream中元素的汇总的方法。
//以下将将字符串累加到ArrayList中: 
   List<String> asList = stringStream.collect(Collectors.toList());  
//以下将按城市分类Person对象: 
   Map<String, List<Person>> peopleByCity = personStream.collect(Collectors.groupingBy(Person::getCity)); 
//以下将按国家和城市对Person对象进行分类,将两个Collector组合在一起: 
   Map<String, Map<String, List<Person>>> peopleByStateAndCity = personStream.collect(Collectors.groupingBy(Person::getState, Collectors.groupingBy(Person::getCity)));

7、其他

  • mapToInt(ToIntFunction<? super T> mapper): 返回括号给定函数应用在这个流的元素的结果 例如:.mapToInt(Integer::intValue)
  • IntStream peek(Consumer<? super T> action) : 返回由该流的元素组成的流,另外提供对流上的每个元素的操作,因为元素从结果流中被消耗,所以这是一个中间操作!该操作并不会对流的结果造成干扰是一个非干扰操作。所以这个方法又经常被用作调试,希望在其中查看元素流过的某个特定位置时的情况。
IntStream.of(1, 2, 3, 4)
         .filter(e -> e > 2)
         .peek(e -> System.out.println("Filtered value: " + e))
         .map(e -> e * e)
         .peek(e -> System.out.println("Mapped value: " + e))
         .sum();
  • void forEach() : 对此流中的每一个元素执行一个操作(遍历)。这是一个终端操作。但是对于并行的流来说他并不保证遵守流的遇到顺序,因为这样做会牺牲并行性的好处。
  • void forEachOrdered(IntConsumer action);: 对此流的每一个元素执行一个操作,保证每一元素都按遇到的顺序处理,流具有定义的遇到顺序。这是一个终端的操作。
  • int[] toArray(); : 返回由该流元素组成的int数组,这是一个终端操作
  • int sum();: 返回此流中的元素总和,这是一个归约的特例。是一个终端操作。
//相当于
return reduce(0,Integer::sum);
  • Stream<Integer> boxed();:返回一个由该流的元素组成的流,每个元素都装箱成为一个整数,这是一个中间操作。返回:与此流的元素一致的Stream,每个元素都装箱成一个Integer
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

聪明不喝牛奶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值