一、简介
函数式接口(Functional Interfaces):如果一个接口定义个唯一一个抽象方法,那么这个接口就成为函数式接口。同时,引入了一个新的注解:@FunctionalInterface。可以把他它放在一个接口前,表示这个接口是一个函数式接口。这个注解是非必须的,只要接口只包含一个方法的接口,虚拟机会自动判断,不过最好在接口上使用注解 @FunctionalInterface 进行声明。在接口中添加了 @FunctionalInterface 的接口,只允许有一个抽象方法,否则编译器也会报错。
@FunctionalInterface
public interface FunctionInterfaceDemo {
void say(String name);
}
同时,Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法。还可以存在静态的方法。
@FunctionalInterface
public interface FunctionInterfaceDemo {
void say(String name);
default void print(String text) {
System.out.println(text);
};
static void write(String message) {
System.out.println(message);
}
}
二、语法
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
(parameters) -> expression
或
(parameters) ->{ statements; }
以下是lambda表达式的重要特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
三、实例
public class LambdaDemo {
public static void main(String[] args) {
// 不需要参数,返回5
ReturnFive m1 = () -> 5;
// 接收一个参数,返回其2倍的值
Multiply m2 = (num) -> 2 * num;
// 接收两个参数,返回他们的差
Subtract m3 = (x, y) -> x - y;
System.out.println(m1.returnFive());
System.out.println(m2.multiply(2));
System.out.println(m3.subtract(4, 4));
}
}
interface ReturnFive {
int returnFive();
}
interface Multiply {
int multiply(int num);
}
interface Subtract {
int subtract(int x, int y);
}
四、变量的作用域
lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
- 在Lambda表达式使用中,Lambda表达式外面的局部变量会被JVM隐式的编译成final类型,Lambda表达式内部只能访问,不能修改;
- Lambda表达式内部对静态变量和成员变量是可读可写的;
public class VariableScopeDemo {
static int a = 1;
public static void main(String[] args) {
VariableScope vs = (n) -> {
a = 2;
return a * n;
};
System.out.println(vs.add(2));
}
}
interface VariableScope {
int add(int n);
}
五、方法的引用
在lambda表达式中,方法引用是一种简化写法,引用的方法就是Lambda表达式的方法体的实现 。
语法:类名::方法名
方法引用一般分为三类:
5.1静态方法引用
5.2实例方法引用
5.3构造方法引用
public class MethodRefDemo {
public static void main(String[] args) {
// 1、传统方式
Factory f = new Factory() {
@Override
public Parent create(String name, int age) {
return new Boy(name, age);
}
};
Parent parent = f.create("小明", 18);
System.out.println(parent.toString());
// 2、lambda表达式
Factory f_ = Boy::new;
Parent parent_ = f_.create("小红", 20);
System.out.println(parent_.toString());
}
}
@FunctionalInterface
interface Factory {
Parent create(String name, int age);
}
class Parent {
String name;
int age;
@Override
public String toString() {
return "Parent [name=" + name + ", age=" + age + "]";
}
}
class Boy extends Parent {
public Boy(String name, int age) {
this.name = name;
this.age = age;
}
}
六、stream相关操作
Lambda为java8带来了闭包,支持对集合对象的stream进行函数式操作, stream api被集成进了collection api ,允许对集合对象进行批量操作。
Stream表示数据流,它没有数据结构,本身也不存储元素,其操作也不会改变源Stream,而是生成新Stream。作为一种操作数据的接口,它提供了过滤、排序、映射、规约等多种操作方法,这些方法按照返回类型被分为两类:凡是返回Stream类型的方法,称之为中间方法(中间操作),其余的都是完结方法(完结操作)。完结方法返回一个某种类型的值,而中间方法则返回新的Stream。
Stream的使用过程有着固定的模式:
1.创建Stream
2.通过中间操作,对原始Stream进行“变化”并生成新的Stream
3.使用完结操作,生成最终结果
6.1中间操作
6.1.1过滤(filter)
结合Predicate接口,Filter对流对象中的所有元素进行过滤,该操作是一个中间操作,这意味着你可以在操作返回结果的基础上进行其他操作
public static void main(String[] args) {
List<String> languages = Arrays.asList("Java", "html5", "JavaScript", "C++", "hibernate", "PHP");
// 开头是J的语言
filter(languages, (name) -> name.startsWith("J"));
// 5结尾的
filter(languages, (name) -> name.endsWith("5"));
// 所有的语言
filter(languages, (name) -> true);
// 一个都不显示
filter(languages, (name) -> false);
// 显示名字长度大于4
filter(languages, (name) -> name.length() > 4);
System.out.println("-----------------------");
// 名字以J开头并且长度大于4的
Predicate<String> c1 = (name) -> name.startsWith("J");
Predicate<String> c2 = (name) -> name.length() > 4;
filter(languages, c1.and(c2));
// 名字不是以J开头
Predicate<String> c3 = (name) -> name.startsWith("J");
filter(languages, c3.negate());
// 名字以J开头或者长度小于4的
Predicate<String> c4 = (name) -> name.startsWith("J");
Predicate<String> c5 = (name) -> name.length() < 4;
filter(languages, c4.or(c5));
// 名字为Java的
filter(languages, Predicate.isEqual("Java"));
// 判断俩个字符串是否相等
boolean test = Predicate.isEqual("hello").test("world");
System.out.println(test);
}
public static void filter(List<String> languages, Predicate<String> condition) {
for (String name : languages) {
if (condition.test(name)) {
System.out.println("==========================");
System.out.println(name + " ");
}
}
}
6.1.2排序(sorted)
结合Comparator,该操作返回一个排序过后的流的视图,原始流的顺序不会改变。通过Comparator来指定排序规则,默认是自然排序
List<String> list = Arrays.asList("a1", "a2", "a3", "b1", "b2", "b3");
// 倒序
list.stream().sorted((s1,s2)->s2.compareTo(s1)).forEach(System.out::println);
// 也可以这样写
Collections.sort(list, (String s1, String s2) -> {
return s1.compareTo(s2);
});
// 简写:
Collections.sort(list, (s1, s2) -> s1.compareTo(s2));
List<Map<String,Integer>> 根据Map.key进行排序:
// List<Map<String,Integer>> 根据Map.key进行排序
public static void main(String[] args) {
List<Map<String,Integer>> list = new ArrayList<>();
list.add(Collections.singletonMap("bily", 3));
list.add(Collections.singletonMap("alice", 2));
list.add(Collections.singletonMap("allen", 1));
list.add(Collections.singletonMap("tony", 6));
list.add(Collections.singletonMap("nice", 5));
list.add(Collections.singletonMap("miky", 4));
list.stream().sorted((m1, m2) -> {
return m1.keySet().iterator().next().compareTo(m2.keySet().iterator().next());
}).collect(Collectors.toList())
.forEach(System.out::println);;
}
结果展示:
Map<String,Integer> 根据key进行排序:
// Map<String,Integer> 根据key进行排序
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("bily", 3);
map.put("alice", 2);
map.put("tony", 6);
map.put("miky", 4);
map.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(System.out::println);
}
结果展示:
6.1.3映射(map)
结合Function接口,该操作能将流对象中的每一个元素映射为另一个元素,实现元素类型的转换。
public class MapedDemo {
public static void main(String[] args) {
List<Employee> list = new ArrayList<>();
list.add(new Employee().builder().id(1L).empName("jack").createTime(new Date()).build());
list.add(new Employee().builder().id(2L).empName("tom").createTime(new Date()).build());
list.add(new Employee().builder().id(3L).empName("allen").createTime(new Date()).build());
list.add(new Employee().builder().id(4L).empName("rock").createTime(new Date()).build());
// 将Employee转化成SysUser
List<SysUser> userList = list.stream().map(TranUtils::trans).collect(Collectors.toList());
// 将list转map
Map<Long, Employee> objMap = list.stream().collect(Collectors.toMap(Employee::getId, emp -> emp));
// 将list转map
Map<Long, String> strMap = list.stream().collect(Collectors.toMap(Employee::getId, Employee::getEmpName));
// Collector.toMap另一个问题,Map中的key不能重复,如果重复的话,会抛出异常:
// java.lang.IllegalStateException: Duplicate key The Fellowship of the Ring
Map<Long, Employee> objMap2 = list.stream().collect(Collectors.toMap(Employee::getId, emp -> emp,(existing, replacement) -> existing));
// 将List 转换 ConcurrentHashMap
Map<Long, Employee> conMap = list.stream().collect(Collectors.toMap(Employee::getId, emp -> emp,(s1, s2) -> s1,ConcurrentHashMap::new));
}
}
class TranUtils {
public static SysUser trans(Employee emp) {
return new SysUser().builder().id(emp.getId()).username(emp.getEmpName()).build();
}
}
6.2完结操作方法
6.2.1匹配(match)
6.2.2收集(collect)
在对经过变换后,将变换的stream元素收集,比如将这些元素存在集合中,可以使用stream提供的collect方法.
6.2.3规约(reduce)
6.2.4计数(count)
6.2.4分组(groupBy)
groupBy多个属性:
// groupBy多个属性
private static String groupByMutilKeys(BizOrder order) {
return order.getCustomerId() + "#" + order.getStatus();
}
public static void main(String[] args) throws ParseException, InterruptedException {
List<BizOrder> orderList = new ArrayList<>();
BizOrder bizOrder1 = BizOrder.builder().customerId(1L).status("SUCCESS").isValid(true).build();
BizOrder bizOrder2 = BizOrder.builder().customerId(1L).status("SUCCESS").isValid(false).build();
BizOrder bizOrder3 = BizOrder.builder().customerId(2L).status("FAIL").isValid(false).build();
BizOrder bizOrder4 = BizOrder.builder().customerId(2L).status("SUCCESS").isValid(true).build();
BizOrder bizOrder5 = BizOrder.builder().customerId(3L).status("FAIL").isValid(true).build();
BizOrder bizOrder6 = BizOrder.builder().customerId(3L).status("FAIL").isValid(false).build();
orderList.add(bizOrder1);
orderList.add(bizOrder2);
orderList.add(bizOrder3);
orderList.add(bizOrder4);
orderList.add(bizOrder5);
orderList.add(bizOrder6);
Map<String, List<BizOrder>> map = orderList.stream().collect(Collectors.groupingBy(o->groupByMutilKeys(o)));
for(String key : map.keySet()) {
System.out.println("key = " + key);
List<BizOrder> subOrderList = map.get(key);
System.out.println(subOrderList);
}
6.3聚合操作
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(4);
list.add(6);
list.add(8);
int sum = list.stream().mapToInt(i->{return i;}).sum();
System.out.println(sum); // 输出20