Java lambda表达式

lambda表达式是Java 8 中的一个很重要的新特性,它容许将行为传到函数中。在Java 8 之前,如果我们想要把行为传到函数中,仅有的选择就是匿名内部类。但Java 8 发布以后,lambda表达式将大量替代匿名内部类的使用,简化代码,突出匿名内部类中最重要的逻辑代码。下面以Runable接口说明这一点:

Runnable runnable = new Runnable() {
      @Override
      public void run() {
        System.out.println(Thread.currentThread().getName());
      }
    };

使用lambda表达式简化:

Runnable runnable1 = () -> System.out.println(Thread.currentThread().getName());

一、lambda表达式简介

lambda表达式是一个匿名函数,一种没有声明的方法,没有修饰符,返回值声明和名称。Java中的lambda表达式通常使用语法是(parameters) -> (expression)(parameters) ->{ statements; },例如:

// 接收两个int型参数,返回它们的值
(int a, int b) -> {  return a + b; }
// 无参数
() -> System.out.println("Hello World");
// 接收一个String型字符串,打印字符串
(String s) -> { System.out.println(s); }
// 无参数 返回 42
() -> 42
// 无参数,返回3.1415
() -> { return 3.1415 };

lambda表达式的结构说明:

  • Lambda 表达式可以具有零个,一个或多个参数;
  • 可以显式声明参数的类型,也可以由编译器自动从上下文推断参数的类型。例如 (int a) 与刚才相同 (a);
  • 参数用小括号括起来,用逗号分隔。例如 (a, b) 或 (int a, int b) 或 (String a, int b, float c);
  • 空括号用于表示一组空的参数。例如 () -> 42;
  • 当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 a -> return a*a;
  • Lambda 表达式的正文可以包含零条,一条或多条语句;
  • 如果 Lambda 表达式的正文只有一条语句,则大括号可不用写,且表达式的返回值类型要与匿名函数的返回类型相同;
  • 如果 Lambda 表达式的正文有一条以上的语句必须包含在大括号(代码块)中,且表达式的返回值类型要与匿名函数的返回类型相同。

二、lambda表达式示例

1. 使用Java 8 lambda表达式进行事件处理

java 8 之前:

JButton show =  new JButton("Show");
    show.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        System.out.println("Hello world");
      }
    });

java 8 :

show.addActionListener((e) -> {
    System.out.println("Hello world");
});
2. 使用Java 8 lambda表达式进行列表迭代

java 8 之前:

List<String> items = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String item : items) {
  System.out.println(item);
}

java 8 :

items.forEach(item -> System.out.println(item));

java 8 之前:

Map<String,String> map = new HashMap <>();
map.put("1","a");
map.put("2","b");
map.put("3","c");
for(String key : map.keySet()){
  System.out.println(map.get(key));
}

java 8 :

map.forEach((k,v)->System.out.println(map.get(k)));
3. 使用lambda表达式和函数式接口Predicate进行逻辑操作

Java 8 添加了一个 java.util.function 的包。它包含了很多类,用来支持Java的函数式编程。其中一个便是Predicate,使用 java.util.function.Predicate 函数式接口以及lambda表达式,可以向API方法添加逻辑,用更少的代码支持更多的动态行为。

public static void main(String[] arg){
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

    System.out.print("输出所有数字:");
    evaluate(list, (n) -> true);

    System.out.print("不输出:");
    evaluate(list, (n) -> false);

    System.out.print("输出偶数:");
    evaluate(list, (n) -> n % 2 == 0);

    System.out.print("输出奇数:");
    evaluate(list, (n) -> n % 2 == 1);

    System.out.print("输出大于 5 的数字:");
    evaluate(list, (n) -> n > 5);
  }
  public static void evaluate(List<Integer> list, Predicate<Integer> predicate) {
//    for (Integer n : list) {
//      if (predicate.test(n)) {
//        System.out.print(n + " ");
//      }
//    }
    list.stream().filter(name -> predicate.test(name)).forEach(name -> System.out.println(name+" "));
  }
4. 多个Predicate合并使用

java.util.function.Predicate允许将两个或更多的 Predicate 合成一个。它提供类似于逻辑操作符AND和OR的方法,名字叫做and()、or()和xor(),用于将传入 filter() 方法的条件合并起来。例如,要得到所有以J开始,长度为四个字母的语言,可以定义两个独立的 Predicate 示例分别表示每一个条件,然后用 Predicate.and() 方法将它们合并起来,如下所示:

List<String> list = Arrays.asList("Java", "C", "C++", "Ruby", "C#", "Go");

Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
list.stream()
        .filter(startsWithJ.and(fourLetterLong))
        .forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));
5. Java 8中使用lambda表达式的Map和Reduce

java.util.stream.Stream接口 和 Lambda 表达式一样,都是 Java 8 新引入的。所有 Stream 的操作必须以 Lambda 表达式为参数。Stream 接口中带有大量有用的方法,比如 map() 的作用就是将 input Stream 的每个元素映射成output Stream 的另外一个元素。

下面的例子,我们将 Lambda 表达式 x -> x*x 传递给map()方法,将其应用于流的所有元素。之后,我们使用forEach打印列表的所有元素:
java 8 之前:

List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
for(Integer n : list) {
    int x = n * n;
    System.out.println(x);
}

java 8 :

List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
list.stream().map((x) -> x*x).forEach(System.out::println);

下面的示例中,我们给定一个列表,然后求列表中每个元素的平方和。这个例子中,我们使用了 reduce() 方法,这个方法的主要作用是把 Stream 元素组合起来:
java 8 之前:

List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = 0;
for(Integer n : list) {
    int x = n * n;
    sum = sum + x;
}
System.out.println(sum);

java 8 :

List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
System.out.println(sum);
6. 通过过滤创建一个String列表

过滤是Java开发者在大规模集合上的一个常用操作,而现在使用lambda表达式和流API过滤大规模数据集合是惊人的简单。流提供了一个 filter() 方法,接受一个 Predicate 对象,即可以传入一个lambda表达式作为过滤逻辑。下面的例子是用lambda表达式过滤Java集合,将帮助理解。

List<String> strList = Arrays.asList("Java", "C", "C++", "Ruby", "C#", "Go");
// 创建一个字符串列表,每个字符串长度大于2
List<String> filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());
System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);
7. 去重操作

本例展示了如何利用流的 distinct() 方法来对集合进行去重:

List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
List<Integer> distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
System.out.printf("Original List : %s,  Square Without duplicates : %s %n", numbers, distinct);
8. 计算集合元素的最大值、最小值、总和以及平均值

IntStream、LongStream 和 DoubleStream 等流的类中,有个非常有用的方法叫做 summaryStatistics()。可以返回 IntSummaryStatistics、LongSummaryStatistics 或者 DoubleSummaryStatistic s,描述流中元素的各种摘要数据。在本例中,我们用这个方法来计算列表的最大值和最小值。它也有 getSum() 和 getAverage() 方法来获得列表的所有元素的总和及平均值。

//获取数字的个数、最小值、最大值、总和以及平均值
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容