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且最大的字母。
Java类库里有一些常用的API,可以了解下
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);
}
}
/**
* 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());
}
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());
}
这个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());
}