Java Lambda表达式

2020-04-14  本文已影响0人  光光小哥

1 简介

Lambda表达式是java8提供的新特性,是一种匿名函数,也是函数式接口实现的快捷方式,类似js中的闭包,它允许把函数当做参数来使用,是面向函数式编程的思想,Lambda的格式为: (参数) -> {方法体},具有如下特征:

// 1 无参数的lambda表达式,返回值5  
() -> 5  
  
// 2 一个数值类型的参数,返回其2倍的值  
x -> 2 * x  
  
// 3 两个数值类型的参数,返回差值  
(x, y) -> x – y  
  
// 4 string类型的参数,在控制台打印,无返回类型  
(String s) -> System.out.print(s)

java是一个面向对象的语言,而Lambda表达式却是一个匿名函数,因此java把Lambda表达式抽象成一个匿名内部类,并没有破坏面向对象的特性。

2 函数式接口

java8中通过注解@FunctionalInterface标明接口为一个函数式接口,函数式接口是只包含一个抽象方法声明的接口,但是允许有默认实现的方法(比如: java.util.function.BinaryOperator)。如果定义了多个抽象方法,编译器会报错,如下图。所以,当我们的代码中定义了函数式接口的时候,要记得加上@FunctionalInterface注解,防止被其他人增加额外的方法而破坏函数式接口的规范。


错误示范

java.lang.Runnable 就是一种函数式接口,每个 Lambda 表达式都能隐式地赋值给函数式接口,例如,我们可以通过 Lambda 表达式创建 Runnable 接口的引用:

Runnable r = () -> System.out.println("hello world");

当不指明函数式接口时,编译器会自动进行类型推断,如下面代码,编译器会自动根据线程类的构造函数签名 public Thread(Runnable r) { },将该 Lambda 表达式赋给 Runnable 接口:

new Thread(
   () -> System.out.println("hello world")
).start();

下表列举了一些常用的函数式接口

接口 参数 返回值 示例
Consumer<T> T void Consumer<Integer> c = (int x) -> { System.out.println(x) };
Supplier<T> None T Supplier<Person> supplier = Person::new;
Function<T,R> T R Function<String, Integer> fuc = Integer::parseInt;
BiFunction<T, R, U> T, R U BiFunction<Integer, Integer, Integer> fuc = (x, y) -> x + y;
Predicate<T> T boolean Predicate<Person> p = (Person person) -> { "zhangsan".equals(person.getName()) };

3 Lambda表达式的作用

Lambda表达式可以让代码更加简练,具有可读性。
举个栗子,这个例子大家都在举,当创建新线程时,不使用lambda的情况下:

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("为了这一行有用的代码,写了那么多多余的行:(  ");
    }
}).start();

使用lambda表达式:

new Thread( () -> System.out.println("一起摇摆~~   ") ).start();

4 配合Stream让代码整洁到飞起

Stream是Java 8 API添加的一个新的抽象,称为流Stream,可以让我们以一种声明的方式处理数据。Stream类似SQL的存储过程,提供了一种对 Java 集合运算和表达的高阶抽象。

Stream的构成

当我们使用一个流的时候,通常包括三个步骤:

// method 1
        Stream<String> stream1 = Stream.of("1", "2", "3");

        // method 2
        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        Stream<String> stream2 = list.stream();

        // method 3
        Stream<String> stream3 = list.parallelStream();

        // method 4
        Stream<String> stream4 = Arrays.stream(new String[]{"1", "2"});

        // method 5
        IntStream stream5 = IntStream.range(0, 100);

流的操作

使用

1 forEach(Consumer<T> action)

public static void main(String[] args) {
        // 输出Stream中的元素
        Stream.of("1","2","3","4","5").forEach(e->System.out.println(e));
}

2 map(Function<T, R> mapper)

public static void main(String[] args) {
         Stream.of("1","2","3","4","5")
                .map(Integer::parseInt) //转成int
                .forEach(System.out::println);
}

3 flatMap(Function<T, Stream<R>> mapper): 会将每一个输入对象输入映射为一个新集合,然后把这些新集合连成一个大集合

public static void main(String[] args) {
        Stream.of("a-b-c-d","e-f-g-h")
                .flatMap(e->Stream.of(e.split("-")))
                .forEach(e->System.out.print(e));

}
// abcdefgh

4 limit(long maxSize)

public static void main(String[] args) {
        Stream.of(1,2,3,4,5,6)
                .limit(3) 
                .forEach(e->System.out.println(e)); 
}
// 输出前三个 1,2,3

5 distinct()

Stream.of(1,1,2,3)
                .distinct() //去重
                .forEach(e->System.out.println(e));
// 1 2 3

6 filter(Predicate<T> predicate)

// 可以用and()、or()逻辑函数来合并Predicate
Predicate<String> p1 = (n) -> n.startsWith("chang"); //Predicate: 验证传进来的参数符不符合规则, 返回true|false
Predicate<String> p2 = (n) -> n.length() == 5;
names.stream()
    .filter(p1.and(p2))
    .forEach((n) -> System.out.print(n));

7 forEachOrdered(Predicate<T> predicate): 适用用于并行流的情况下进行迭代,能保证迭代的有序性

Stream.of(0,1,2,3,4,5,6,7,8,9)
                .parallel()
                .forEachOrdered(e->{
                    System.out.println(Thread.currentThread().getName()+": "+e);});

8 Collectors: 将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
 
System.out.println("筛选列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);

// 筛选列表: [abc, bc, efg, abcd, jkl]
// 合并字符串: abc, bc, efg, abcd, jkl


List<Person> personList = Lists.newArrayList();

// 同名分组
Map<String, List<Person>> map = personList.stream().collect(Collectors.groupingBy(Person::getName));

// 根据唯一id映射, 保证Person::getId是唯一的,否则报java.lang.IllegalStateException
Map<Integer, Person> map = personList.stream().collect(Collectors.toMap(Person::getId, p -> p));

9 更多例子

String k = "key";
HashMap<String, Integer> map = new HashMap<>() ;
map.put(k, 1);
map.merge(k, 2, (oldVal, newVal) -> oldVal + newVal);

// 等同如下代码
String k = "key";
HashMap<String, Integer> map = new HashMap<>() ;
map.put(k, 1);
int newVal = 2;
if(map.containsKey(k)) {
    map.put(k, map.get(k) + newVal);
} else {
    map.put(k, newVal);
}

复合lambda表达式

1 比较器复合

可以使用静态方法Comparator.comparing定义比较器,根据提取用于比较的键值的Function来返回一个Comparator,
如: Comparator<Apple> cmp = Comparator.comparing(Apple::getWeight),该表达式表示比较苹果的重量,如果想对苹果按重量递减排序,此时无需重新定义比较器,接口有一个默认方法reversed可以使给定的比较器逆序。因此仍然用开始的那个比较器,只要简单修改修改一下前一个例子就可以对苹果按重量递减排序: appleList.sort(cmp.reversed());

2 谓词复合

谓词接口包括三个方法:negate、and和or,让我们可以重用已有的Predicate来创建更复杂的谓词,如4.3.6中的filter方法

3 函数复合

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g); // andThen方法会返回一个函数,它先对输入应用一个给定函数,再对输出应用另一个函数
int result = h.apply(1); // 返回结果为4

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.compose(g); // compose方法,先把给定的函数用作compose的参数里面给的那个函数,然后再把函数本身用于结果
int result = h.apply(1);  // 返回结果为3
函数复合

总结

List<String> title = Arrays.asList("1", "2", "3");
Stream<String> s = title.stream();
s.forEach(System.out::println);
s.forEach(System.out::println); // Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
上一篇 下一篇

猜你喜欢

热点阅读