设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

Java 8为什么需要Lambda表达式

2013-4-7 11:28| 发布者: joejoe0332| 查看: 1856| 评论: 0|原作者: oschina|来自: oschina

摘要:   函数编程在C#、Python、JavaScript中都得到充分体现。而Java直到最新的Java 8才开始正式支持函数编程,最明显的改进就是对Lamba表达式的支持。正如C#之父Anders Hejlsberg在那篇文章编程语言大趋势中所讲,未来 ...
  函数编程在C#、Python、JavaScript中都得到充分体现。而Java直到最新的Java 8才开始正式支持函数编程,最明显的改进就是对Lamba表达式的支持。正如C#之父Anders Hejlsberg在那篇文章 编程语言大趋势 中所讲,未来的编程语言将逐渐融合各自的特性,而不存在单纯的声明式语言(如之前的Java)或者单纯的函数编程语言。将来声明式编程语言借鉴函数编程思想,函数编程语言融合声明式编程特性...这几乎是一种必然趋势。如下图所示:

                                      

                                                影响力较大的三个趋势

    那具体而言我们为什么需要Lambda表达式呢?难道Java的OO和命令式编程(imperative programming)特性不够强大吗?下面让我们来分析下其原因。

1、内部循环和外部循环

     先看一个大家耳熟能详的例子:

1List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
2 
3for (int number : numbers) {
4    System.out.println(number);
5}
    是不是很常见呢?这个叫外部循环(External Iteration)。但是外部循环有什么问题呢?简单来说存在下面三个缺点:
  1. 只能顺序处理List中的元素(process one by one)
  2. 不能充分利用多核CPU
  3. 不利于编译器优化

    而如果利用内部循环,代码写成下面这样:

1List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
2 
3numbers.forEach((Integer value) -> System.out.println(value));
    这样就能规避上面的三个问题:
  1. 不一定需要顺序处理List中的元素,顺序可以不确定
  2. 可以并行处理,充分利用多核CPU的优势
  3. 有利于JIT编译器对代码进行优化

    类似的C#从4.0版本开始也支持集合元素并行处理,代码如下:

1List<int> nums = new List<int> { 1, 2, 3, 4, 5, 6 };
2Parallel.ForEach(nums, (value) =>
3{
4   Console.WriteLine(value);
5});

2、传递行为,而不仅仅是传值

     如果你使用C#有一段时间的话,那么你很可能已经明白这个标题的意思了。在C#中,经常看到一些函数的参数是Action或者Func类型,比如下面这个:

01public class ArticleDac {
02   ...
03   public Article GetArticles(Func<IDbSet<Article>, Article> func)   // 这里传递的就是行为
04   {
05      using(var db = xx) {
06         return func(db.Articles);
07      
08   }
09   ...
10}
11// 下面是调用
12int articleId = 119;
13var firstArticle = new ArticleDac().GetArticles(
14    articleDbSet =>
15    articleDbSet.AsQueryable().FirstOrDefault(x => x.id == articleId)
16);
     看不懂?没关系。我们先来看一个体现传值局限性的场景吧,上代码:
1List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
2 
3public int sumAll(List<Integer> numbers) {
4    int total = 0;
5    for (int number : numbers) {
6        total += number;
7    }
8    return total;
9}
     sumAll算法很简单,完成的是将List中所有元素相加。某一天如果我们需要增加一个对List中所有偶数求和的方法sumAllEven,如下:
1public int sumAllEven(List<Integer> numbers) {
2    int total = 0;
3    for (int number : numbers) {
4        if (number % 2 == 0) {
5            total += number;
6        }
7    }
8    return total;
9}
     又有一天,我们需要增加第三个方法:对List中所有大于3的元素求和,那是不是继续加下面的方法呢?
1public int sumAllEven(List<Integer> numbers) {
2    int total = 0;
3    for (int number : numbers) {
4        if (number > 3) {
5            total += number;
6        }
7    }
8    return total;
9}

     比较这三个方法,我们发现了一个很明显的“代码臭味”—— 代码重复(详情参考《重构》),三个方法的唯一区别在于if判断这一行代码。如果脱离这里的上下文,我们会怎么做呢?我首先会先想到利用策略模式重构代码如下:

01public interface Strategy {
02   public boolean test(int num);
03}
04 
05public class SumAllStrategy implements Strategy {
06   public boolean test(int num) {
07      return true;
08   }
09}
10 
11public class SumAllEvenStrategy implements Strategy {
12   public boolean test(int num) {
13      return num % 2 == 0;
14   }
15}
16 
17public class ContextClass {
18   private Strategy stragegy = null;
19   private final static Strategy DEFAULT_STRATEGY = new SumAllStrategy();
20 
21   public ContextClass() {
22      this(null);
23   }
24 
25   public ContextClass(Stragegy stragegy) {
26      if(strategy != null) {
27         this.strategy = strategy;
28      }
29      else {
30         this.strategy = DEFAULT_STRATEGY;
31      }
32   }
33 
34   public int sumAll(List<Integer> numbers) {
35      int total = 0;
36      for (int number : numbers) {
37         if (strategy.test(number)) {
38            total += number;
39         }
40      }
41 
42      return total;
43   }
44}
45 
46 
47// 调用
48ContextClass context = new ContextClass();
49context.sumAll(numbers);

     设计模式在这里发挥了作用,OO特性还是蛮强大的!但这是唯一的解决方案吗(当然不考虑用其他设计模式来解决,因为都是OO范畴!)?当然有,该轮到Java 8 Lambda表达式中的谓词(Predicate)发挥作用了!

01public int sumAll(List<Integer> numbers, Predicate<Integer> p) {
02    int total = 0;
03    for (int number : numbers) {
04        if (p.test(number)) {
05            total += number;
06        }
07    }
08    return total;
09}
10 
11sumAll(numbers, n -> true);
12sumAll(numbers, n -> n % 2 == 0);
13sumAll(numbers, n -> n > 3);
      代码是不是比上面简洁很多了?语义应该也很明确,就不多解释了,如果实在看不懂,请参考我的另外一篇文章: http://www.cnblogs.com/feichexia/archive/2012/11/15/Java8_LambdaExpression.html 从这里也可以看出未引入Lambda表达式之前的Java代码的冗长(Java这点被很多人诟病)。

     当然C#早已经支持这种用法,用C#改写上面的代码如下:

1public int SumAll(IEnumerable<int> numbers, Predicate<int> predicate) {    
2   return numbers.Where(i => predicate(i)).Sum();
3}
4 
5SumAll(numbers, n => true);
6SumAll(numbers, n => n % 2 == 0);
7SumAll(numbers, n => n > 3);


酷毙

雷人

鲜花

鸡蛋

漂亮
  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部