Lambda表达式与函数式编程探索

文章目录

    • 导论
      • 函数式编程
      • 相关链接
      • 参考文献
    • Java Lambda Build Block
      • 1. 静态方法式Lambda
      • 2. 集合
        • 选出符合条件的元素
        • 所有唯一元素
        • Top N
        • Bottom N
        • 流转换
          • 怎么将一个数组流转换为数组包含的元素流?

导论

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 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());
    }

你可能感兴趣的:(Java)