本次项目搭建为Maven项目 jdk 为1.8 ,当然用 普通的java 项目也可以。
这段时间看虚拟机介绍的时候发现1.8 有一个函数编程,然后看到说函数编程是一个高级编程(也有可能是开玩笑的)就觉得应该挺好用的,然后就查阅资料,看到了函数编程的第一步就是 要会用 lambad 表达式。
看到网上有个例子说是用lambda 实现了一个按钮的监听事件,我觉得这个例子很好,为了能够理解这个例子我还特意从网上查阅了一下资料。我直接贴代码吧。
这个是窗体类
import javax.swing.SwingUtilities;
import swing.swing.window.IndexFrame;
/**
* Hello world!
*
*/
public class App {
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
// 用java 默认的窗口模式,不设置就是 用系统自带的
//JFrame.setDefaultLookAndFeelDecorated(true);
IndexFrame index = new IndexFrame(500, 500, "你好世界");
index.show();
}
});
// lambda 表达式形式
SwingUtilities.invokeLater(() -> {
IndexFrame index = new IndexFrame(500, 500, "你好世界");
index.show();
});
//
Runnable runnable1 = () -> {
IndexFrame index = new IndexFrame(500, 500, "你好世界");
index.show();
};
SwingUtilities.invokeLater(runnable1);
}
}
这里绑定事件的时候 传递的是 参数 -> 结果
接下来是启动类
package swing.swing;
import javax.swing.SwingUtilities;
import swing.swing.window.IndexFrame;
/**
* Hello world!
*
*/
public class App {
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
// 用java 默认的窗口模式,不设置就是 用系统自带的
//JFrame.setDefaultLookAndFeelDecorated(true);
IndexFrame index = new IndexFrame(500, 500, "你好世界");
index.show();
}
});
// 现在先不用管,只是先贴出来,后边会详细说明
// lambda 表达式形式
SwingUtilities.invokeLater(() -> {
IndexFrame index = new IndexFrame(500, 500, "你好世界");
index.show();
});
//
Runnable runnable1 = () -> {
IndexFrame index = new IndexFrame(500, 500, "你好世界");
index.show();
};
SwingUtilities.invokeLater(runnable1);
}
}
可以看到控制台每次单机按钮都会打印两次绑定的单击事件,现在完成了 Lambda 的初次使用,接下来了解一下怎么正确使用吧。
首先看到第一个lambda 表达式以后先用一下,我们知道list 排序的时候需要实现一个Comparator 接口的compare 方法。代码如下
List<Integer> list = new ArrayList<Integer>() {{add(1);add(2);add(4);add(3);}};
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o1-o2;
}
});
System.out.println(list);
list.sort((o1,o2) -> o2-o1);
System.out.println(list);
执行结果
可以看到 lambda 表达式很轻松的就重写了这个方法。个人理解,他可以去轻松的重写任何一个要重写的方法,(通俗点就是 用这个重写类的某一个方法实现这个功能,仅仅重写这一个方法的时候,多个方法的时候他可能会找不到要重写的方法,毕竟他也是代码写出来的,不会那么只能,目前没有很好的例子,以后有例子了我再补充上去,)
进入正题:
lambda 的几种表现形式:
// 重写现成启动的时候的run方法, 这个是不带参数的
Runnable noArguments = () -> System.out.println("Hello World");
// 重写的时候方法体不值一行的时候 如果有返回值 加return
Runnable noArgumentss = () ->{
System.out.println("Hello World");
System.out.println("Hello World");
// 方法有返回值的时候
// return "";
}
// 还有就是刚才绑定单击事件的时候,只有一个参数的 可以省略 ()
ActionListener oneArgument = event -> System.out.println("button clicked");
// 顺便提到,如果只有一行代码,并且是返回值的结果 就比如说重写了一个方法 直接就是 return a; 可以这样写
// BinaryOperator 这个是jdk 1.8 提供的储存函数的 类 可以存放函数并且定义返回值
BinaryOperator<Long> add = (x, y) -> x + y; ?
BinaryOperator<Long> addExplicit = (Long x, Long y) -> x + y;
下面看一下这个 1.8 提供的这个函数类 BinaryOperator 怎么使用
BinaryOperator<Integer> add = new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer t, Integer u) {
Integer n1 = Integer.parseInt(t.toString());
Integer n2 = Integer.parseInt(u.toString());
System.out.println(" n1+n2="+ (n1+n2));
return (n1+n2);
}
};
// lambda 自己推断类型
BinaryOperator<Integer> add1 = (n1,n2) ->{
System.out.println("1: n1+n2="+ (n1+n2));
return (n1+n2);
};
// 显示声明类型
BinaryOperator<Integer> add2 = (Integer n1,Integer n2) ->{
System.out.println("2: n1+n2="+ (n1+n2));
return (n1+n2);
};
add.apply(1, 2);
add1.apply(1, 2);
add2.apply(1, 2);
可以看出来 用了 lambda 表达式是多么的方便。
Stream 是java 1.8 新引入的抽象操作集合的类
之前我们统计一个县的同名人数,要写一个循环,然后遍历这个循环,求count
List<String> countyNames = new ArrayList<String>(){{
add("张三");add("李四");add("李四");
add("李四");add("张三");add("王五");
add("王五四");add("李四");add("张四");
add("张三");add("王五");add("王五");
add("李四");add("张四");add("王五");
add("aa");add("bb");add("cc");
add("aa");add("bb");add("cc");
add("aa");add("bb");add("cc");
}};
int count = 0;
for(String str : countyNames) {
if(str.trim().indexOf("张") != -1)
count ++;
}
System.out.println("姓张的个数为:"+count);
最后的结果是 5
但是 用 Stream 就可以减少很多代码
// stream 筛选
Long count1 = countyNames.stream()
.filter(str -> str.trim().indexOf("张") != -1)
.filter(str -> str.trim().equals("张四"))
.count();
System.out.println("stream: 筛选的结果:"+count1);
这样就不仅少了很多代码,代码结构也变得更容易理解。
感兴趣的人可以去看一下他们的源码。大致就是用一个迭代器去实现的
上面的式子用了两个筛选,这样并不会造成循环两次的性能浪费,因为 filter 是一个惰性方法,
后面的 count 方法被称为 及早求值 方法
顾名思义,惰性求职方法 就是不会产生新集合,及早求值方法就是会产生结果的方法,怎么区分它们呢,
如果返回值是 Stream ,那么是惰性求值;
如果返回值是另一个值或为空,那么就是及早求值
看一段代码
countyNames.stream().filter(str -> {
System.out.println("我是惰性方法");
return true;
});
countyNames.stream().filter(str -> {
System.out.println("我是调用了及早求值所以我执行了");
return true;
}).count();
可以看到程序并没有走惰性方法,根本就没执行,只有他碰到求值方法才会返回。不得不佩服jdk 的开发人员的头脑。
看一段代码
List<String> newNames = countyNames.stream()
.map(str -> str+ "1")
.collect(Collectors.toList());
System.out.println(newNames.toString());
想必看到这个代码就已经大概知道这个代码的用处了,他们的运行结果是:
这就是接下来要说的 map 方法 和 toList 方法
我们经常在写代码的时候要给某一些 集合 元素做一些修改这时候就用到map方法来操作数据的元素,看到我这里是给他拼接了一个 1
方法内部的 lambda 表达式收到一个 参数 对应集合类型,返回的也是集合类型,这里要注意一下。
min max
Optional<Integer> opt1 = list.stream()
.min(Comparator.comparing(number -> number));
// 这个是根据 字符串的长度找最大了,也可以返回string 类型的按照字典排序
Optional<String> opt = countyNames.stream()
.max(Comparator.comparing(str -> str.toString().length()));
System.out.println(opt.get()+ "\n" +opt1.get());
进行自定义计算两个之间的值,可用于数字
// 计算数字的和 ,这个是计算的总和
System.out.println(
list.stream()
.reduce(-10,(n1,n2)-> n1+n2)
);
// 拼接全部的字符
System.out.println(
countyNames.stream()
.reduce("names : ", (n1,n2)->n1+n2)
);
我把这个单独列出来是因为他跟之前的不一样,看代码
List<Integer> list3 = Stream.of(
new ArrayList<Integer>() {{add(1);add(2);}},
new ArrayList<Integer>() {{add(3);add(4);}})
.flatMap(numbers -> numbers.stream())
.collect(Collectors.toList());
System.out.println(list3.toString());
这里我是用 Stream 生成了两个 集合的stream 然后调用 这个 flatMap 方法,该方法仅限于多个列表的流, 把多个列表拼接到一起。