Lambda表达式

导论

Java 8引入流机制和Lambda表达式之后,两者配合地可谓是天衣无缝,本来想看看Java流库有什么东西,可是全是Lambda表达式,晕死,看来得好好了解一下Lambda了!

函数式编程

在文献2中,作者由Swing中的ActionListener来引入Lambda的概念。比如:

   button.addActionListener(new ActionListener() {
     	public void actionPerformed(ActionEvent event) {
     		System.out.println("button clicked");
        }
    });

这个语法是不知道从什么时候引入的Java匿名类,在上述代码里,它新创建了一个ActionListener类,并重写了actionPerformed的方法。实际上ActionListener。这个类只有这一个方法,目的就是让此类的使用者重写方法来完善按钮按下的具体动作。

ActionListener这种接口其实功能相当单一,其除了System.out…那行代码其他的全部可以通过接口声明由编译器自动推断。那么,这个模板代码是一个冗余的存在。Lambda的目的就是干掉它。哦,对了,ActionListener就是函数式接口,函数式接口简单说就是只有一个方法的接口。

那么接下来的代码可能大多数人都见过:

 button.addActionListener(event->System.out.println("button clicked"));

解析一下它的结构,event是参数,->代表这是一个Lambda表达式,后面是逻辑语句,多上代码的话可以用{}来包围。
那么event->System.out.println("button clicked")这块代码其实就代替了以上的匿名内部类,并且返回的同样也是一个ActionListener,因此可以是这个样子。

  ActionListener listener=event->System.out.println("button clicked")

Lambda表达式是代码块,类型就是函数式接口,我之前看到过很多类似的函数式接口如Predicate,Consumer等,部分还应用到了泛型。

但是Lambda表达式在我初学时,根本就没有办法看。因为看不懂,不过现在我可以在脑海里构造这样一个映射:

    event->System.out.println("button clicked")

相当于

    new ActionListener() {
         	public void actionPerformed(ActionEvent event) {
         		System.out.println("button clicked");
            }
        }

为了深入理解这一点,可以自己构造一个类似Comsumer等JDK内置函数式接口。
以我的英文名Oneslide Icyater为例

    public interface Oneslide {
         void callmyname();
    }

这个接口只有一个方法,因此这个是个合法的函数式接口,接下来写个Main证明一下:


public class Main {

    public static void main(String args[]) {
       Oneslide icywater=()->{
           System.out.println("oneslide icywater");
       };
       icywater.callmyname();
    }

}

你会得到oneslide icywater这样的输出。可见,Lambda并非什么秘密武器,它只是一个语法糖。前面也说到会和泛型一起用,那么把上面例子变得再复杂一点:

public interface Oneslide<T> {
     String callmyname(T target);
}

上面的接口有了返回值和泛型


public class Main {

    public static void main(String args[]) {
        Oneslide icywater=(name)->{
            String returnName=(String)name+" icywater";
            //简单输出一下吧,不然有点尴尬
            System.out.println(returnName);
            return returnName;
        };
        icywater.callmyname("oneslide");
    }

}

虽然上面的例子是一个很挫地使用了泛型,但是能够充分证明其用法,那么剩下的可以利用
面向对象的概念对语法进行解释了,简化一下代码:

至于为什么引入Lambda呢?我猜大概是其写出来代码所表现出的简洁性超过其不易读的特点。

那么其应用于Stream库

    String name="oneslide icywater";
    System.out.println(name.chars().filter((w)->w>100).max());

如果你查看filter的声明你会发现Predicate,这里的(w)->w>100)作为一个匿名的Predicate实现类。这几行语句的意思找到编码大于100且最大的字母。

相关链接

  1. Stream并行流性能测试

参考文献

  1. Mastering Lambdas: Java Programming in a Multicore World- Maurice Naftalin
  2. Java 8 Lambdas: Functional Programming For The Masses -Richard Warburton
  3. Core Java Volume II -10th edition

Java Lambda Build Block

Java类库里有一些常用的API,可以了解下

1. 静态方法式Lambda

public class LambdaTest {
	// Lambda功能接口实现
    public static String staticMethod(String s){
        System.out.println(s);
        return  s;
    }
    public static void main(String[] args) {
        ArrayList<String> list=new ArrayList<>(10);
        list.add("string");
        list.add("facek");
        // 引用本类的静态方法
        list.forEach(LambdaTest::staticMethod);
    }
}

其中public void forEach(Consumer<? super E> action)用到Consumer接口就是无返回值的功能接口。

@FunctionalInterface
public interface Consumer<T> {
    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
}    

传入的LambdaTest::staticMethod可以想象成:

new Consumer<String>(){
	void accept(String t){
		LambdaTest.staticMethod(t);
	}
}

2. 集合

选出符合条件的元素
   /**
     * remove all element in a list which does not match the predicate
     * @param list collection source
     * @param predicate match criteria
     * @return a collection in which all elements match the specified predicate
     * **/
    public static <T> List<T> filterMatch(List<T> list, Predicate<T> predicate){
        return list.stream().filter(predicate).collect(Collectors.toList());
    }

比如选出长度大于10的字符串:

   public static void main(String[] args) {
        List<String> list= Arrays.asList("oneslideicywater","testertester","onelisde");
        //这里传入一个predicate判断传入参数(String类型)s是否长度大于10
        System.out.println(Lambda.filterMatch(list,s->s.length()>10)); 
    }
所有唯一元素
    /**
     * remove duplicate element in the list
     * @param list collection source
     * @return a collection in which all elements are different from each other
     * **/
    public static <T> List<T> filterUnique(List<T> list){
        return list.stream().distinct().collect(Collectors.toList());
    }

比如筛选全部不同的数字:

  /**
     * remove duplicate element in the list
     * @param list collection source
     * @return a collection in which all elements are different from each other
     * **/
    public static <T> List<T> filterUnique(List<T> list){
        return list.stream().distinct().collect(Collectors.toList());
    }
Top N

limit可以限制流中保留元素的数量,如下就是将流中元素降序排序得到前N名。

   /**
     * top N
     * @param list collection source
     * @return a sorted list with n elements, -1 represent keep all elements
     * **/
    public static <E extends Comparable<E>> List<E> topN(List<E> list,int maxSize){
        if (maxSize<0) maxSize=list.size();
        if (maxSize==0) {
            return Collections.emptyList();
        }
        return list.stream().sorted(Comparator.reverseOrder()).limit(maxSize).collect(Collectors.toList());
    }
Bottom N

这个demo很不自然,不过足以说明skip的用法,skip和limit是互补的,skip是跳过前N个。

    /**
     * top N
     * @param list collection source
     * @return a sorted list with n elements, -1 represent keep all elements
     * **/
    public static <E extends Comparable<E>> List<E> bottomN(List<E> list,int maxSize){
        if (maxSize<0) return list.stream().sorted().collect(Collectors.toList());
        if (maxSize==0) {
            return Collections.emptyList();
        }
        return list.stream().sorted(Comparator.reverseOrder()).skip(list.size()-maxSize).collect(Collectors.toList());
    }

上面的选出倒数N名。

流转换

模拟SQL,选出一个表里的所有列。

新建一个Pig对象,里面有很多列假设(这里只写一列)

public class Pig {

    String name;

    public Pig(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

map将一个Pig流转换为String流,还可以经过经过多重转换。

 List<Pig> list=Arrays.asList(new Pig("hello"),new Pig("what's"));
 // 这里map对每个元素调用其getter
 System.out.println(list.stream().map(Pig::getName).collect(Collectors.toList()));
 // map还可以链式调用,多次转换
 System.out.println(list.stream().map(Pig::getName).map(String::length).collect(Collectors.toList()));
怎么将一个数组流转换为数组包含的元素流?

你有多个数组,数组里面包含元素的类型都是一样的,请将多个数组合并为一个数组并去重。

flatMap返回的是一个Stream流,里面包含的是所有数组的S元素放到一个数组里。

   // transform stream
    public static <S> List<S> mergeMultipleAndGetUniquer(List<S[]> list){
        return list.stream().flatMap(Arrays::stream).distinct().collect(Collectors.toList());
    }
并行流

并行流可以提高进程执行的过程,但是必须是线程安全的。
下面程序并行读取文件和串行读取

public class CountLongWords {
    public static void main(String[] args) {
        String contents=null;

        {
            try {
                contents = new String(Files.readAllBytes(Paths.get("D:\\workbunch\\lambdaTest\\wordSource.html")));
            } catch (IOException e) {
                e.printStackTrace();
            }
            // serial
            List<String> words=  Arrays.asList(contents.split(" "));
            long begin=System.currentTimeMillis();
            long count=words.stream().filter(word->word.length()>12).count();
            System.out.println(System.currentTimeMillis()-begin+count-count);
            // parallel
            long beginParall=System.currentTimeMillis();
            count =words.parallelStream().filter(word->word.length()>12).count();
            System.out.println(System.currentTimeMillis()-beginParall+count/count-1);
        }
    }


}

Optional

如何支持链式调用

public class DomainContainer {

    public static class Person {
        private Car car=new Car();
        public Optional<Car> getCar() { return Optional.ofNullable(car); }
    }
    public static class Car {
        private Insurance insurance=null;
        public Optional<Insurance> getInsurance() { return Optional.ofNullable(insurance); }
    }
    public static class Insurance {
        // a insurance must have a name
        private String name="pacific";
        public String getName() { return name; }
    }

    public static void main(String[] args) {
        Optional<Person> p=Optional.of(new Person());
        // return unknown if one of Optional in the chain is empty
        String a=p.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("unknown");
        System.out.println(a);
    }
}

序列化问题

Optional不支持序列化 ,仅是为了支持链式调用风格加入的一个对象。因此最好就是领域对象还是原来那样设置,不过暴露出一个返回Optional<T>的Getter,比如:

 public Optional<Insurance> getInsurance() {
  return Optional.ofNullable(insurance); 
 }
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值