java8新特性详解

Lambda表达式

Lambda表达式介绍

Lambda是一个匿名函数,可以理解为一段可以传递的代码。

简化匿名内部类的使用,语法更加简单。

Lambda的标准格式

Lambda省去面向对象的条条框框,Lambda的标准格式格式由3个部分组成:

(参数类型 参数名称) -> { 代码体; }

格式说明:

  • (参数类型 参数名称):参数列表

  • {代码体;}:方法体

  • -> :箭头,分隔参数列表和方法体

Lambda省略格式

在Lambda标准格式的基础上,使用省略写法的规则为:

  • 小括号内参数的类型可以省略

  • 如果小括号内有且仅有一个参数,则小括号可以省略

  • 如果大括号内有且仅有一个语句,可以同时省略大括号、return关键字及语句分号

例如:

(int a) ->{
	return new Person();
}

省略后

a -> new Person()

接口新增两个方法

JDK8以前的接口:

interface 接口名 { 
  静态常量; 
  抽象方法; 
}

JDK 8对接口的增强,接口还可以有默认方法静态方法

JDK 8的接口:

interface 接口名 { 
  静态常量; 
  抽象方法; 
  默认方法; 
  静态方法; 
}

接口引入默认方法

接口中的默认方法实现类不必重写,可以直接使用,实现类也可以根据需要重写。这样就方便接口的扩展。

接口默认方法的定义格式

interface 接口名 { 
  修饰符 default 返回值类型 方法名() { 
    			代码; 
  	} 
}

接口静态方法

为了方便接口扩展,JDK 8为接口新增了静态方法。

接口静态方法的定义格式

interface 接口名 { 
  修饰符 static 返回值类型 方法名() { 
    代码; 
  } 
}

接口静态方法的使用

直接使用接口名调用即可:接口名.静态方法名();

常用内置函数式接口

它们主要在 java.util.function 包中。下面是最常用的几个接口。

  1. Supplier接口
  2. Consumer接口
  3. Function接口
  4. Predicate接口

Supplier接口

java.util.function.Supplier 接口,它意味着"供给" , 对应的Lambda表达式需要“对外提供”一个符合泛型类

型的对象数据。

@FunctionalInterface 
public interface Supplier<T> { 
  public abstract T get(); 
}

供给型接口,通过Supplier接口中的get方法可以得到一个值,无参有返回的接口。

Consumer接口

java.util.function.Consumer 接口则正好相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛

型参数决定。

@FunctionalInterface 
public interface Consumer<T> { 
  public abstract void accept(T t); 
}

要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。

Function接口

java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,

后者称为后置条件。有参数有返回值。

@FunctionalInterface 
public interface Function<T, R> { 
  public abstract R apply(T t); 
}

Function转换型接口,对apply方法传入的T类型数据进行处理,返回R类型的结果,有参有返回的接口。

Predicate接口

有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用

java.util.function.Predicate 接口。

@FunctionalInterface 
public interface Predicate<T> { 
  public abstract boolean test(T t); 
}//Predicate接口用于做判断,返回boolean类型的值

方法引用

方法引用格式

符号表示 ::

符号说明 : 双冒号为方法引用运算符,而它所在的表达式被称为方法引用

应用场景 : 如果Lambda所要实现的方案 , 已经有其他方法存在相同方案,那么则可以使用方法引用。

常见引用方式

方法引用在JDK 8中使用方式相当灵活,有以下几种形式:

  • instanceName::methodName 对象::方法名

  • ClassName::staticMethodName 类名::静态方法

  • ClassName::methodName 类名::普通方法

  • ClassName::new 类名::new (调用的构造器)

  • TypeName[]::new String[]::new(调用数组的构造器)

对象名::引用成员方法

// 对象::实例方法
Supplier<Long> supp2 = now::getTime; 
System.out.println(supp2.get());

类名::引用静态方法

// 类名::静态方法
Supplier<Long> supp2 = System::currentTimeMillis; 
System.out.println(supp2.get());

类名::引用实例方法

// 类名::实例方法
Function<String, Integer> f2 = String::length; 
System.out.println(f2.apply("abc"));

类名::new 引入构造器

// 类名::new
BiFunction<String, Integer, Person> fun2 = Person::new; 
System.out.println(fun2.apply("张三", 18));

数组::new 引用数组构造器

// 类型[]::new
Function<Integer, String[]> fun2 = String[]::new; 
String[] arr2 = fun.apply(5); 
System.out.println(arr2 + ", " + arr2.length);

Stream流

Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工

处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。

获取Stream流的两种方式

方式一:根据Collection获取

首先, java.util.Collection 接口中加入了default方法 stream 用来获取流,所以其所有实现类均可获取流。

// 集合获取流
// Collection接口中的方法: default Stream<E> stream() 获取流
List<String> list = new ArrayList<>() ;
Stream<String> stream1 = list.stream() ;

Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream() ;

Vector<String> vector = new Vector<>();
Stream<String> stream3 = vector.stream() ;

java.util.Map 接口不是 Collection 的子接口,所以获取对应的流需要分key、value或entry等情况:

// Map获取流
Map<String, String> map = new HashMap<>();

Stream<String> keyStream = map.keySet().stream();
Stream<String> valueStream = map.values().stream();
Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();

方式二:Stream中的静态方法of获取流

由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法 of ,使用很简单:

// Stream中的静态方法: static Stream of(T... values)
Stream<String> stream6=Stream.of("aa","bb","cc");

String[]arr={"aa","bb","cc"};
Stream<String> stream7=Stream.of(arr);

Integer[]arr2={11,22,33};
Stream<Integer> stream8=Stream.of(arr2);

// 注意:基本数据类型的数组不行
int[]arr3={11,22,33};
Stream<int[]>stream9=Stream.of(arr3);

Stream注意事项

  • 终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。本小节中,终结方法包括 count 和

forEach 方法。

  • 非终结方法:返回值类型仍然是 Stream 类型的方法,支持链式调用。(除了终结方法外,其余方法均为非终结方法。)

注意事项

  • Stream只能操作一次

  • Stream方法返回的是新的流

  • Stream不调用终结方法,中间的操作不会执行

Stream常用方法

forEach方法

forEach 用来遍历流中的数据

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

count方法

Stream流提供 count 方法来统计其中的元素个数:

filter方法

fifilter用于过滤数据,返回符合过滤条件的数据

方法声明:

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

limit方法

limit 方法可以对流进行截取,只取用前n个。方法声明:

Stream<T> limit(long maxSize);

skip方法

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

Stream<T> skip(long n);

map方法

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

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

sorted方法

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

Stream<T> sorted(); 
Stream<T> sorted(Comparator<? super T> comparator);

distinct方法

如果需要去除重复数据,可以使用 distinct 方法。

例如:

// 去掉重复,并打印出来
Stream.of(22, 33, 22, 11, 33).distinct().forEach(System.out::println);

如果是自定义类型,根据对象的hashCode和equals来去除重复元素的。

match方法

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

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

find方法

如果需要找到某些数据,可以使用 find 相关方法。

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

调用optional的get方法即可获得返回值

@Test 
public void testFind() { 
  Optional<Integer> first = Stream.of(5, 3, 6, 1).findFirst(); 
  System.out.println("first = " + first.get()); 
  
  Optional<Integer> any = Stream.of(5, 3, 6, 1).findAny(); 
  System.out.println("any = " + any.get()); 
}

max和min方法

Stream流中的 max 和 min 相关方法基本使用的代码如:

@Test 
public void testMax_Min() { 
  Optional<Integer> max = Stream.of(5, 3, 6, 1).max((o1, o2) -> o1 - o2); 
  System.out.println("first = " + max.get()); 
  Optional<Integer> min = Stream.of(5, 3, 6, 1).min((o1, o2) -> o1 - o2); 
  System.out.println("any = " + min.get()); 
}

reduce方法

如果需要将所有数据归纳得到一个数据,可以使用 reduce 方法,他的执行过程如图。

在这里插入图片描述

    @Test
    public void testReduce() {
        // 计算总值
        int reduce = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> {
            System.out.println("x = " + x + ", y = " + y);
            return x + y;
        });
        System.out.println("reduce = " + reduce); // 21

        // 获取最大值
        Integer max = Stream.of(4, 5, 3, 9).reduce(0, (x, y) -> {
            return x > y ? x : y;
        });
        System.out.println("max = " + max);
    }

maptoInt方法

如果需要将Stream中的Integer类型数据转成int类型,可以使用 mapToInt 方法。

IntStream mapToInt(ToIntFunction<? super T> mapper);

contact方法

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

    @Test
    public void testContact() {
        Stream<String> streamA = Stream.of("张三");
        Stream<String> streamB = Stream.of("李四");

        // 合并成一个流
        Stream<String> newStream = Stream.concat(streamA, streamB);
        // 注意:合并流之后,不能操作之前的流啦.
        // streamA.forEach(System.out::println);

        newStream.forEach(System.out::println);
    }

Stream流的返回结果

将结果整合到集合中

Stream流提供 collect 方法,其参数需要一个 java.util.stream.Collector<T,A, R> 接口对象来指定收集到哪

种集合中。java.util.stream.Collectors 类提供一些方法,可以作为 Collector`接口的实例:

public static <T> Collector<T, ?, List<T>> toList() // 转换为 List 集合。
  public static <T> Collector<T, ?, Set<T>> toSet() // 转换为 Set 集合。

下面是这两个方法的基本使用代码:

    @Test
    public void testStreamToCollection() {
        Stream<String> stream = Stream.of("aa", "bb", "cc", "bb");

        // 将流中数据收集到集合中
        // collect收集流中的数据到集合中
         List<String> list = stream.collect(Collectors.toList());

         Set<String> set = stream.collect(Collectors.toSet());

        // 收集到指定的集合中ArrayList
         ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));

        HashSet<String> hashSet = stream.collect(Collectors.toCollection(HashSet::new));

    }

Stream流结果到数组中

Stream<String> stream = Stream.of("aa", "bb", "cc");
String[] strings = stream.toArray(String[]::new);

对流中数据进行聚合计算

当我们使用Stream流处理数据后,可以像数据库的聚合函数一样对某个字段进行操作。比如获取最大值,获取最小值,求总和,平均值,统计数量。

对流中数据进行分组

当我们使用Stream流处理数据后,可以根据某个属性将数据分组:

    @Test
    public void testGroup() {
        Stream<Student> studentStream = Stream.of(
                new Student("赵丽颖", 52, 95),
                new Student("杨颖", 56, 88),
                new Student("迪丽热巴", 56, 55),
                new Student("柳岩", 52, 33));

        // 将分数大于60的分为一组,小于60分成另一组
        Map<String, List<Student>> map = studentStream.collect(Collectors.groupingBy((s) -> {
            if (s.getSocre() > 60) {
                return "及格";
            } else {
                return "不及格";
            }
        }));

        map.forEach((k, v) -> {System.out.println(k + "::" + v); });
    }

说明

这个笔记是我学习黑马程序员教程做的笔记,方便自己以后复习查阅。


版权声明:本文为haqiang555原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。