Java Lambda 函数式编程例子详解
一.Lambda表达式
1. Java为何需要Lambda
- 1996年1月,Java 1.0发布了,此后计算机编程领域发生了翻天覆地的变化。商业发展需要更复杂的应用,大多数程序都跑在更强大的装备多核CPU的机器上。带有高效运行期编译器的Java虚拟机(JVM)的出现,使得程序员将精力更多放在编写干净、易于维护的代码上,而不是思考如何将每一个CPU时钟、每一字节内存物尽其用。
- 多核CPU的出现成了“房间里的大象”,无法忽视却没人愿意正视。算法中引入锁不但容易出错,而且消耗时间。人们开发了java.util.concurrent包和很多第三方类库,试图将并发抽象化,用以帮助程序员写出在多核CPU上运行良好的程序。不幸的是,到目前为止,我们走得还不够远。
- 那些类库的开发者使用Java时,发现抽象的级别还不够。处理大数据就是个很好的例子,面对大数据,Java还欠缺高效的并行操作。Java 8允许开发者编写复杂的集合处理算法,只需要简单修改一个方法,就能让代码在多核CPU上高效运行。为了编写并行处理这些大数据的类库,需要在语言层面上修改现有的Java:增加lambda表达式。
当然,这样做是有代价的,程序员必须学习如何编写和阅读包含lambda表达式的代码,但是,这不是一桩赔本的买卖。与手写一大段复杂的、线程安全的代码相比,学习一点新语法和一些新习惯容易很多。开发企业级应用时,好的类库和框架极大地降低了开发时间和成本,也扫清了开发易用且高效的类库的障碍。
2. Lambda应用场景及示例
- 2.1使用() -> {} 替代匿名类
//Before Java 8:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Before Java8 ");
}
}).start();
//Java 8 way:
new Thread(() -> System.out.println("In Java8!"));
// Before Java 8:
JButton show = new JButton("Show");
show.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("without lambda expression is boring");
}
});
// Java 8 way:
show.addActionListener((e) -> {
System.out.println("Action !! Lambda expressions Rocks");
});
- 2.2使用内循环替代外循环
List features = Arrays.asList("Lambdas", "Default Method", "Stream API",
"Date and Time API");
features.forEach(n -> System.out.println(n));
//输出
Lambdas
Default Method
Stream API
Date and Time API
- 2.3支持函数编程、管道处理数据
Stream:代表通用的引用类型的流式操作接口
IntStream:代表int类型的流式操作接口,避免了int和Integer之间频繁的拆装箱操作
LongStream:代表long类型的流式操作接口,避免了long和Long之间频繁的拆装箱操作
DoubleStream:代表double类型的流式操作接口,避免了double和Double之间频繁的拆装箱操作
可创建Stream的数据源
集合:Collection.stream()或Collection.parallelStream()
数组:Arrays.stream()
对象(或基本数据)序列:Stream.of(),IntStream.of(),LongStream.of(),DoubleStream.of()
字符流:BufferdReader.lines()
文件路径:Files.walk(),Files.lines(),Files.find()
随机数流:Random.ints()
其他:BitSet.stream(),Pattern.splitAsStream(),JarFile.stream()等
示例
/**
*
* Javalambda:使符合条件的String相加
*/
List strings = Arrays.asList("first","second","third","firefox");
//strings.forEach(System.out::println);
String finalString = (String) strings.stream().filter(name-> name.toString().length()<6)
.collect(Collectors.joining(","));
System.out.println(finalString);
//输出
first,third
/**
*
* 运用Predicate在filter中增加condition
*/
List strings = Arrays.asList("first","second","third","firefox");
Predicate<String> startsWithJ = (n) -> n.startsWith("f");
Predicate<String> fourLetterLong = (n) -> n.length() >= 4;
strings.stream()
.filter(startsWithJ.and(fourLetterLong))
.forEach((n) -> System.out.print(n + " "));
//输出
first firefox
/**
*
* map()函数将stream流中每个对象做相应的改变,reduce()函数将所有值合成一个
*/
List<BigDecimal> prices = Arrays.asList(new BigDecimal(70),new BigDecimal(80),new BigDecimal(90));
final BigDecimal totalOfDiscountedPrices = prices.stream()
.filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0)
.map(price -> price.multiply(BigDecimal.valueOf(0.9)))
.reduce(BigDecimal.ZERO,BigDecimal::add);
System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);
//输出
Total of discounted prices: 216.0
//新方法:get()方法提取Optional中的Integer(实现每个数组加上12再一起相加)
List<Integer> costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
int bill = costBeforeTax.stream().map((cost) -> cost + 12).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);
//输出
Total : 1560
//获取数字的个数、最小值、最大值、总和以及平均值
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());
//输出
Highest prime number in List : 29
Lowest prime number in List : 2
Sum of all prime numbers : 129
Average of all prime numbers : 12.9
二.函数式接口
- 函数式接口,首先是一个接口,然后就是在这个接口里面只能有一个抽象方法,但是可以有多个非抽象方法的接口。
Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。
函数式接口可以被隐式转换为 lambda 表达式。
@FunctionalInterface
interface defaultMethod {
void apply(String obj);
default void say(String name) {
System.out.println("函数式接口中的" + name);
}
}
public class FuntionalInterfaceTest {
public static void main(String[] args) {
defaultMethod i = (o) -> {
System.out.println("函数式接口中的"+o);
};
i.apply("抽象方法");
i.say("普通方法");
}
}
//输出
函数式接口中的抽象方法
函数式接口中的普通方法