函数式编程将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。
函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。
和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。和过程化编程相比,函数式编程里函数的计算可随时调用。
使用java实现累加。
import java.util.Arrays;
import java.util.List;
class Accumulation {
public static Integer sum(List<Integer> nums) {
int result = 0;
for (Integer num : nums) {
result += num;
}
return result;
}
public static void main(String[] args) {
List<Integer> nums = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(sum(nums)); // 55
}
}
同样的代码,用 Java8 Stream 实现。
Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.stream()
.reduce(0, Integer::sum);
java实现。
class Fibonacci {
public static int fibonacci(int number) {
if (number == 1) {
return 1;
}
if(number == 2) {
return 2;
}
int a = 1;
int b = 2;
for(int cnt = 3; cnt <= number; cnt++) {
int c = a + b;
a = b;
b = c;
}
return b;
}
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
System.out.print(fibonacci(i) + ", ");
}
}
}
// 1, 2, 3, 5, 8, 13, 21, 34, 55, 89,
用 Java8 Stream 实现。
Stream.iterate(new int[]{1, 1}, s -> new int[]{s[1], s[0] + s[1]})
.limit(10)
.map(n -> n[1])
.collect(Collectors.toList())
// [1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
纯函数是指执行过程中没有副作用的函数,所谓副作用是说超出函数控制的操作,比如在执行过程中操作文件系统、数据库等外部资源。
纯函数还具有引用透明性的特点,也就是同样的输入导致同样的输出,以至于完全可以用函数的值代替对函数的调用。
一般来说,有两种改变数据的方式。第一种方式是直接修改变量的值,第二种方式是使用新的一份数据替换旧数据。
不可变对象(Immutable Object):对象一旦被建立后,对象全部的状态及属性在其生命周期内不会发生任何变化。
谈到不可变性,我们做个游戏:统计在座的人数。
我们都知道从某个人开始依次报数,最后得到的数字就是总人数,其实这就是一种不可变计算的游戏,为什么这么说呢?因为报数其实一个计算的过程,第一个人计算出1这个数,传递给第二个人。然后第二个人拿着前面的1进行加一操作,然后把结果2传递给后面的人做加法,以此类推。
为了提高统计的效率,我也可以进行分组,然后每组自行报数,最后统计结果。
但是如果我在白板上写个数字1,然后让大家过来改这个数字,很大可能会出现错误,因为这个数字成为了竞争条件。在多并发的情况下,就得用读写锁来控制。所以不可变性特别利于并发。
一般来讲,建立不可变类原则有如下几条:
Lambda Calculus 在数学定义上面非常简单,并且和与图灵机等价,也就是说FP语言程序与普通的命令式语言如C可以写出同样能力的程序。lambda算子理论一切都是函数。
Lambda演算在编程语言之中是一个编程范式,它遵循如下几个特点:
Java 8开始引入 Lambda 表达式,使代码更加简洁。
Lambda 表达式包含三个要素:
当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口。
没有使用Lambda:
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent actionEvent){
System.out.println("Action detected");
}
});
使用Lambda:
button.addActionListener( actionEvent -> {
System.out.println("Action detected");
});
让我们来看一个更明显的例子。
不采用Lambda:
Runnable runnable1 = new Runnable(){
@Override
public void run(){
System.out.println("Running without Lambda");
}
};
使用Lambda:
Runnable runnable2 = ()->System.out.println("Running from Lambda");
从示例可以看到,使用Lambda表达式不仅让代码变得简单,而且可读,最重要的是代码量也随之减少很多。
(形式参数)-> { 代码块 }
省略规则:
注意事项:
Runnable r =() ->System.out.println(“Lambda表达式”)
;new Thread(()->System.out.println(“Lambda表达式”)).start()
;所需类型不同:
使用限制不同:
实现原理不同:
常见的引用方式:
函数式接口:有且仅有一个抽象方法的接口。
Java中的函数式编程体现就是Lambda表达式,所以函数式接口就可以使用于Lambda使用的接口。
只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。
如何检测一个接口是不是函数式接口呢?
Java 8 在java.util.function包下预定了大量的函数式接口,常用如下:
Stream是Java 8 API添加的一个新的抽象,称为流Stream,以一种声明性方式处理数据集合(侧重对于源数据计算能力的封装,并且支持序列与并行两种操作方式)。
Stream流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据结构并不保存数据,它的主要目的在于计算。
Stream流是对集合(Collection)对象功能的增强,与Lambda表达式结合,可以提高编程效率、间接性和程序可读性。
stream流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
对Stream的中间操作,可以视为是源的查询,并且是惰式设计,对于源数据进行的计算只有在需要时才会被执行,与数据库中视图的原理相似。
Stream流的强大之处在于提供了丰富的中间操作,相比集合或数组这类容器,极大的简化源数据的计算复杂度。
一个流可以跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。
这类操作都是惰性的,仅仅调用到这类方法,并没有真正开始流的遍历,真正的遍历需等到终端操作时,常见的中间操作有 filter、map 等。
Stream流执行完终端操作之后,无法再执行其他动作,否则会报状态异常,提示该流已经被执行操作或者被关闭,想要再次执行操作必须重新创建Stream流。
一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据再生成流。
终端操作的执行,才会真正开始流的遍历。如 count、collect 等。
Stream代码示例如下:
import java.util.*;
import java.util.stream.Collectors;
public class StreamTests {
public static void main(String[] args) {
List<User> userList = getUserList();
// filter:输出ID大于6的user对象
// collect:收集器,将流转换为其他形式
System.out.println("----------filter--------");
List filetrUserList = userList.stream()
.filter(user -> user.getId() > 6l)
.collect(Collectors.toList());
// forEach:遍历流
filetrUserList.forEach(System.out::println);
// map 映射成一个新的元素
System.out.println("-------map------------");
List mapUserList = userList.stream()
.map(user -> user.getUsername() + "用户")
.collect(Collectors.toList());
mapUserList.forEach(System.out::println);
// distinct:去重
System.out.println("-------distinct------------");
List distinctUsers = userList.stream()
.map(User::getCity)
.distinct()
.collect(Collectors.toList());
distinctUsers.forEach(System.out::println);
// sorted:排序,根据名字倒序
System.out.println("-------sorted------------");
userList.stream()
.sorted(Comparator.comparing(User::getUsername).reversed())
.collect(Collectors.toList())
.forEach(System.out::println);
// limit:取前5条数据
System.out.println("-----limit--------");
userList.stream()
.limit(5)
.collect(Collectors.toList())
.forEach(System.out::println);
// skip:跳过第几条取后几条
System.out.println("--------skip--------");
userList.stream()
.skip(7)
.collect(Collectors.toList())
.forEach(System.out::println);
// flatMap:数据拆分一对多映射
System.out.println("--------flatMap--------");
userList.stream()
.flatMap(user -> Arrays.stream(user.getCity().split(",")))
.forEach(System.out::println);
// peek:对元素进行遍历处理,每个用户ID加1输出
System.out.println("--------peek--------");
userList.stream()
.peek(user -> user.setId(user.getId() + 1))
.forEach(System.out::println);
// findFirst:返回第一个元素
System.out.println("--------findFirst--------");
System.out.println(userList.stream()
.findFirst()
.get());
System.out.println("--------findFirst--------");
System.out.println(userList.stream()
.filter(user -> "深圳".equals(user.getCity()))
.findFirst()
.get());
// findAny:将返回当前流中的任意元素
System.out.println("--------findAny--------");
System.out.println(userList.stream()
.findAny()
.get());
System.out.println("--------findAny--------");
System.out.println(userList.stream()
.filter(user -> "深圳".equals(user.getCity()))
.findAny()
.get());
// count:返回流中元素总数
System.out.println("--------count--------");
long count = userList.stream()
.filter(user -> user.getAge() > 20)
.count();
System.out.println(count);
// sum:求和
System.out.println("--------sum--------");
long sum = userList.stream()
.mapToLong(User::getId)
.sum();
System.out.println(sum);
// max:最大值
System.out.println("--------max--------");
long max = userList.stream()
.max(Comparator.comparingLong(User::getId))
.get()
.getId();
System.out.println(max);
// min:最小值
System.out.println("--------min--------");
long min = userList.stream()
.min(Comparator.comparingLong(User::getId))
.get()
.getId();
System.out.println(min);
// anyMatch:检查是否至少匹配一个元素
System.out.println("--------anyMatch--------");
boolean matchAny = userList.stream()
.anyMatch(user -> "北京".equals(user.getCity()));
System.out.println(matchAny);
// allMatch:检查是否匹配所有元素
System.out.println("--------matchAll--------");
boolean matchAll = userList.stream()
.allMatch(user -> "北京".equals(user.getCity()));
System.out.println(matchAll);
// noneMatch:检查是否没有匹配所有元素,返回boolean
System.out.println("--------noneMatch--------");
boolean nonaMatch = userList.stream()
.allMatch(user -> "云南".equals(user.getCity()));
System.out.println(nonaMatch);
// reduce:将流中元素反复结合起来,得到一个值
System.out.println("--------findAny--------");
Optional reduce = userList.stream()
.reduce((user, user2) -> { return user; });
if(reduce.isPresent())
System.out.println(reduce.get());
// Collectors.toList 将用户ID存放到List集合中
System.out.println("--------Collectors.toList--------");
List idList = userList.stream()
.map(User::getId)
.collect(Collectors.toList());
System.out.println(idList);
// Collectors.toMap 将用户ID和username以Key-Value形式存放到Map集合中
System.out.println("--------Collectors.toMap--------");
Map<Long, String> userMap = userList.stream()
.collect(Collectors.toMap(User::getId, User::getUsername));
System.out.println(userMap);
// Collectors.toSet 将用户所在城市存放到Set集合中
System.out.println("--------Collectors.toSet--------");
Set citySet = userList.stream()
.map(User::getCity)
.collect(Collectors.toSet());
System.out.println(citySet);
// Collectors.counting 符合条件的用户总数
System.out.println("--------Collectors.counting--------");
long count1 = userList.stream()
.filter(user -> user.getId()>1)
.collect(Collectors.counting());
System.out.println(count1);
// Collectors.summingLong 对结果元素即用户ID求和
System.out.println("--------Collectors.summingLong--------");
Long sumLong = userList.stream()
.filter(user -> user.getId()>2)
.collect(Collectors.summingLong(User::getId));
System.out.println(sumLong);
// Collectors.minBy 筛选元素中ID最小的用户
System.out.println("--------Collectors.minBy--------");
User minId = userList.stream()
.collect(Collectors.minBy(Comparator.comparingLong(User::getId)))
.get();
System.out.println(minId);
// Collectors.joining 将用户所在城市,以指定分隔符链接成字符串;
System.out.println("--------Collectors.joining--------");
String joinCity = userList.stream()
.map(User::getCity)
.collect(Collectors.joining("||"));
System.out.println(joinCity);
// Collectors.groupingBy 按条件分组,以城市对用户进行分组;
System.out.println("--------Collectors.groupingBy--------");
Map<String,List<User>> groupCity = userList.stream()
.collect(Collectors.groupingBy(User::getCity));
System.out.println(groupCity);
}
private static List<User> getUserList() {
List<User> userList = new ArrayList<>();
userList.add(new User(1l,"Mary",18,"上海"));
userList.add(new User(2l,"John",16,"上海"));
userList.add(new User(3l,"Jerry",20,"上海"));
userList.add(new User(4l,"Siri",22,"北京"));
userList.add(new User(5l,"Mini",15,"深圳"));
userList.add(new User(6l,"Marks",24,"北京"));
userList.add(new User(7l,"Aili",21,"上海"));
userList.add(new User(8l,"Baidu",88,"广州"));
userList.add(new User(9l,"Jin",76,"广州"));
userList.add(new User(10l,"Yi",26,"深圳"));
return userList;
}
}
class User {
private Long id;
private String username;
private Integer age;
private String city;
public User(Long id, String username, Integer age, String city) {
this.id = id;
this.username = username;
this.age = age;
this.city = city;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"id\":")
.append(id);
sb.append(", \"username\":\"")
.append(username).append('\"');
sb.append(", \"age\":")
.append(age);
sb.append(", \"city\":\"")
.append(city).append('\"');
sb.append('}');
return sb.toString();
}
}
结果如下
----------filter--------
{"id":7, "username":"Aili", "age":21, "city":"上海"}
{"id":8, "username":"Baidu", "age":88, "city":"广州"}
{"id":9, "username":"Jin", "age":76, "city":"广州"}
{"id":10, "username":"Yi", "age":26, "city":"深圳"}
-------map------------
Mary用户
John用户
Jerry用户
Siri用户
Mini用户
Marks用户
Aili用户
Baidu用户
Jin用户
Yi用户
-------distinct------------
上海
北京
深圳
广州
-------sorted------------
{"id":10, "username":"Yi", "age":26, "city":"深圳"}
{"id":4, "username":"Siri", "age":22, "city":"北京"}
{"id":5, "username":"Mini", "age":15, "city":"深圳"}
{"id":1, "username":"Mary", "age":18, "city":"上海"}
{"id":6, "username":"Marks", "age":24, "city":"北京"}
{"id":2, "username":"John", "age":16, "city":"上海"}
{"id":9, "username":"Jin", "age":76, "city":"广州"}
{"id":3, "username":"Jerry", "age":20, "city":"上海"}
{"id":8, "username":"Baidu", "age":88, "city":"广州"}
{"id":7, "username":"Aili", "age":21, "city":"上海"}
-----limit--------
{"id":1, "username":"Mary", "age":18, "city":"上海"}
{"id":2, "username":"John", "age":16, "city":"上海"}
{"id":3, "username":"Jerry", "age":20, "city":"上海"}
{"id":4, "username":"Siri", "age":22, "city":"北京"}
{"id":5, "username":"Mini", "age":15, "city":"深圳"}
--------skip--------
{"id":8, "username":"Baidu", "age":88, "city":"广州"}
{"id":9, "username":"Jin", "age":76, "city":"广州"}
{"id":10, "username":"Yi", "age":26, "city":"深圳"}
--------flatMap--------
上海
上海
上海
北京
深圳
北京
上海
广州
广州
深圳
--------peek--------
{"id":2, "username":"Mary", "age":18, "city":"上海"}
{"id":3, "username":"John", "age":16, "city":"上海"}
{"id":4, "username":"Jerry", "age":20, "city":"上海"}
{"id":5, "username":"Siri", "age":22, "city":"北京"}
{"id":6, "username":"Mini", "age":15, "city":"深圳"}
{"id":7, "username":"Marks", "age":24, "city":"北京"}
{"id":8, "username":"Aili", "age":21, "city":"上海"}
{"id":9, "username":"Baidu", "age":88, "city":"广州"}
{"id":10, "username":"Jin", "age":76, "city":"广州"}
{"id":11, "username":"Yi", "age":26, "city":"深圳"}
--------findFirst--------
{"id":2, "username":"Mary", "age":18, "city":"上海"}
--------findFirst--------
{"id":6, "username":"Mini", "age":15, "city":"深圳"}
--------findAny--------
{"id":2, "username":"Mary", "age":18, "city":"上海"}
--------findAny--------
{"id":6, "username":"Mini", "age":15, "city":"深圳"}
--------count--------
6
--------sum--------
65
--------max--------
11
--------min--------
2
--------anyMatch--------
true
--------matchAll--------
false
--------noneMatch--------
false
--------findAny--------
{"id":2, "username":"Mary", "age":18, "city":"上海"}
--------Collectors.toList--------
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
--------Collectors.toMap--------
{2=Mary, 3=John, 4=Jerry, 5=Siri, 6=Mini, 7=Marks, 8=Aili, 9=Baidu, 10=Jin, 11=Yi}
--------Collectors.toSet--------
[上海, 广州, 北京, 深圳]
--------Collectors.counting--------
10
--------Collectors.summingLong--------
63
--------Collectors.minBy--------
{"id":2, "username":"Mary", "age":18, "city":"上海"}
--------Collectors.joining--------
上海||上海||上海||北京||深圳||北京||上海||广州||广州||深圳
--------Collectors.groupingBy--------
{广州=[{"id":9, "username":"Baidu", "age":88, "city":"广州"}, {"id":10, "username":"Jin", "age":76, "city":"广州"}], 上海=[{"id":2, "username":"Mary", "age":18, "city":"上海"}, {"id":3, "username":"John", "age":16, "city":"上海"}, {"id":4, "username":"Jerry", "age":20, "city":"上海"}, {"id":8, "username":"Aili", "age":21, "city":"上海"}], 深圳=[{"id":6, "username":"Mini", "age":15, "city":"深圳"}, {"id":11, "username":"Yi", "age":26, "city":"深圳"}], 北京=[{"id":5, "username":"Siri", "age":22, "city":"北京"}, {"id":7, "username":"Marks", "age":24, "city":"北京"}]}
java的lambda、链式调用、函数式编程、stream流都是非常有用的,在代码中经常都会遇到,可以提高生产力,让程序员写出高效率、干净、简洁的代码。