@FunctionalInterface
public interface Runnable {
public abstract void run();
}
函数式编程语法,能够精简代码;
方法体为表达式,该表达式的值作为返回值返回
(parameters)-> expression
方法体为代码块,必须用 {} 来包裹起来,且需要一个 return 返回值,但若函数式接口里面方法返回值是 void,则无需返回值。
(parameters) -> {statements;}
例如,下面是使用匿名内部类和Lambda表达式的代码比较。
下面是使用匿名内部类的代码:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.print("Helllo Lambda in actionPerformed");
}
});
下面是使用 Lambda 表达式后:
button.addActionListener(
\\actionPerformed 有一个参数 e 传入,所以用 (ActionEvent e)
(ActionEvent e)-> System.out.print("Helllo Lambda in actionPerformed")
);
上面是方法体包含了参数传入 (ActionEvent e),如果没有参数则只需 ( ),例如 Thread 中的 run 方法就没有参数传入,当它使用 Lambda 表达式后:
Thread t = new Thread(() -> { System.out.println("Hello from a thread in run");}); \\run 没有参数传入,所以用 (), 后面用 {} 包起方法体
通过上面两个代码的比较可以发现使用 Lambda 表达式可以简化代码,并提高代码的可读性。
为了进一步简化 Lambda 表达式,可以使用方法引用。例如,下面三种分别是使用内部类,使用 Lambda 表示式和使用方法引用方式的比较:
//1. 使用内部类
Function<Integer, String> f = new Function<Integer,String>(){
@Override
public String apply(Integer t) {
return null;
}
};
//2. 使用 Lambda 表达式
Function<Integer, String> f2 = (t)->String.valueOf(t);
//3. 使用方法引用的方式
Function<Integer, String> f1 = String::valueOf;
例如,People 类中有一个方法 getMaleList 需要获取男性的列表,这里需要定义一个函数式接口 PersonInterface:
interface PersonInterface {
public boolean test(Person person);
}
public class People {
private List<Person> persons= new ArrayList<Person>();
public List<Person> getMaleList(PersonInterface filter) {
List<Person> res = new ArrayList<Person>();
persons.forEach(
(Person person) -> {
if (filter.test(person)) {//调用 PersonInterface 的方法
res.add(person);}}
);
return res;
}
}
// 为了去除 PersonInterface 这个函数式接口,可以用通用函数式接口 Predicate 替代如下:
class People{
private List<Person> persons= new ArrayList<Person>();
public List<Person> getMaleList(Predicate<Person> predicate) {
List<Person> res = new ArrayList<Person>();
persons.forEach(
person -> {
if (predicate.test(person)) {//调用 Predicate 的抽象方法 test
res.add(person);
}});
return res;
}
}
序号 | 接口 | 描述 |
---|---|---|
1 | BiConsumer |
代表了一个接受两个参数的操作,并且不返回任何结果 |
2 | BiFunction |
代表了一个接受两个输入参数的方法,并且返回一个结果 |
3 | BinaryOperator | 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果 |
4 | BiPredicate |
代表了一个两个参数的boolean值方法 |
5 | BooleanSupplier | 代表了boolean值结果的提供方 |
6 | Consumer | 代表了接受一个输入参数并且无返回的操作 |
7 | DoubleBinaryOperator | 代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。 |
8 | DoubleConsumer | 代表一个接受double值参数的操作,并且不返回结果 |
9 | DoubleFunction | 代表接受一个double值参数的方法,并且返回结果 |
10 | DoublePredicate | 代表一个拥有double值参数的boolean值方法 |
11 | DoubleSupplier | 代表一个double值结构的提供方 |
12 | DoubleToIntFunction | 接受一个double类型输入,返回一个int类型结果 |
13 | DoubleToLongFunction | 接受一个double类型输入,返回一个long类型结果 |
14 | DoubleUnaryOperator | 接受一个参数同为类型double,返回值类型也为double 。 |
15 | Function |
接受一个输入参数,返回一个结果。 |
16 | IntBinaryOperator | 接受两个参数同为类型int,返回值类型也为int |
17 | IntConsumer | 接受一个int类型的输入参数,无返回值 。 |
18 | IntFunction | 接受一个int类型输入参数,返回一个结果 。 |
19 | IntPredicate | 接受一个int输入参数,返回一个布尔值的结果。 |
20 | IntSupplier | 无参数,返回一个int类型结果。 |
21 | IntToDoubleFunction | 接受一个int类型输入,返回一个double类型结果 。 |
22 | IntToLongFunction | 接受一个int类型输入,返回一个long类型结果。 |
23 | IntUnaryOperator | 接受一个参数同为类型int,返回值类型也为int 。 |
24 | LongBinaryOperator | 接受两个参数同为类型long,返回值类型也为long。 |
25 | LongConsumer | 接受一个long类型的输入参数,无返回值。 |
26 | LongFunction | 接受一个long类型输入参数,返回一个结果。 |
27 | LongPredicate | R接受一个long输入参数,返回一个布尔值类型结果。 |
28 | LongSupplier | 无参数,返回一个结果long类型的值 |
29 | LongToDoubleFunction | 接受一个long类型输入,返回一个double类型结果 |
30 | LongToIntFunction | 接受一个long类型输入,返回一个int类型结果。 |
31 | LongUnaryOperator | 接受一个参数同为类型long,返回值类型也为long。 |
32 | ObjDoubleConsumer | 接受一个object类型和一个double类型的输入参数,无返回值。 |
33 | ObjIntConsumer | 接受一个object类型和一个int类型的输入参数,无返回值。 |
34 | ObjLongConsumer | 接受一个object类型和一个long类型的输入参数,无返回值。 |
35 | Predicate | 接受一个输入参数,返回一个布尔值结果。 |
36 | Supplier | 无参数,返回一个结果。 |
37 | ToDoubleBiFunction |
接受两个输入参数,返回一个double类型结果 |
38 | ToDoubleFunction | 接受一个输入参数,返回一个double类型结果 |
39 | ToIntBiFunction |
接受两个输入参数,返回一个int类型结果。 |
40 | ToIntFunction | 接受一个输入参数,返回一个int类型结果。 |
41 | ToLongBiFunction |
接受两个输入参数,返回一个long类型结果。 |
42 | ToLongFunction | 接受一个输入参数,返回一个long类型结果。 |
43 | UnaryOperator | 接受一个参数为类型T,返回值类型也为T。 |
Lambda初探:
public interface Student {
void xuexi();
}
public static void main(String[] args) {
Student student=new Student() {
@Override
public void xuexi() {
System.err.println("学生的每天学习!");
}
};
}
public static void main(String[] args) {
Student student= () -> System.err.println("学生的每天学习!");
}
> 能使用lambda表达式的条件: 1. 是一个接口 2. 只有一个抽象方法 这里因为没有参数所以括号内的内容为空;
Lambda表达式,有参数初探:
public interface Student {
void xuexi(String content);
}
public static void main(String[] args) {
Student student= new Student() {
@Override
public void xuexi(String content) {
System.err.println("学生的"+content+"每天学习!");
}
};
student.xuexi("任务是");
}
public static void main(String[] args) {
Student student= content -> System.err.println("学生的"+content+"每天学习!");
student.xuexi("任务是");
}
什么是函数式接口:一个接口只有一个抽象方法,它就是函数式接口;
什么是函数式编程:满足函数式接口,并且使用此接口用lambda表达式写法时,就用到了函数式编程了;
上面示例已经说明,函数式编程接口都只有一个抽象方法,因此在采用这种写法时,编译器会将这段函数编译后当做该抽象方法的实现。 如果接口与有多个抽象方法,编译器就不知道这段函数式应该实现哪个方法了。
语法:
简略语法:
Consumer c = new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
}
};
Consumer c = (o) -> System.out.println(o);
Consumer c = System.out::println;
System.out 表示要调用的哪个类 :: 后面的表示调用这个类的某个方法; 我们不但可以使用println,还可以使用这个类的其他方法进行替换;
总结:
通过最后一段代码,我们可以简单的理解函数式编程,Consumer接口直接就可以当成一个函数了,这个函数接收一个输入参数,然后针对这个输入进行处理;当然其本质上仍旧是一个对象,但我们已经省去了诸如老方式中的对象定义过程,直接使用一段代码来给函数式接口对象赋值。
而且最为关键的是,这个函数式对象因为本质上仍旧是一个对象,因此可以做为其它方法的参数或者返回值,可以与原有的代码实现无缝集成!
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
public static void consumerTest() {
Consumer f = System.out::println;
Consumer f2 = n -> System.out.println(n + "-F2");
//执行完F后再执行F2的Accept方法
f.andThen(f2).accept("test");
//连续执行F的Accept方法
f.andThen(f).andThen(f).andThen(f).accept("test1");
}
/**
* @program:
* @description:
* @author: anyu
* @create: 2020-03-09
*/
@Data
public class Student implements Serializable {
private String name;
private String age;
}
public static void main(String[] args) {
Consumer consumer = x -> System.out.println(JSON.toJSONString(x));
Student student = new Student();
student.setAge("18");
consumer.accept(student);
}
/**
* Function测试
*/
public static void functionTest() {
Function<Integer, Integer> f = s -> s++;
Function<Integer, Integer> g = s -> s * 2;
/**
* 下面表示在执行F时,先执行G,并且执行F时使用G的输出当作输入。
* 相当于以下代码:
* Integer a = g.apply(1);
* System.out.println(f.apply(a));
*/
System.out.println(f.compose(g).apply(1));
/**
* 表示执行F的Apply后使用其返回的值当作输入再执行G的Apply;
* 相当于以下代码
* Integer a = f.apply(1);
* System.out.println(g.apply(a));
*/
System.out.println(f.andThen(g).apply(1));
/**
* identity方法会返回一个不进行任何处理的Function,即输出与输入值相等;
*/
System.out.println(Function.identity().apply("a"));
}
public static void main(String[] args) {
Function<Student, String> g = Student::getName;
Student student = new Student();
student.setName("张三");
String apply = g.apply(student);
System.err.println(apply);
}
g 是定义的一个方法;第二步创建了一个对象;当把这个对象作为参数调用apply方法执行时,就会获得这个对象的名称作为String返回;拿到这个String返回时并打印到控制台,如图所示:
public static void main(String[] args) {
Function<Student[], String> g = students->students[0].getName() ; // 操作数组,打印数组中的Student对象的getName值
Student student = new Student(); // 定义对象
student.setName("暗余");
Student[] arr=new Student[]{student}; // 定义数组并将此对象传入
String apply = g.apply(arr); // 执行Function方法并获取到返回值
System.err.println(apply); // 打印返回值
}
public static void main(String[] args) {
Function<Integer,Integer> function= x-> x+1;
System.out.println(function.apply(5));
}
结果控制台打印为 : 6
public static void main(String[] args) {
Function<List<Student>,List<String>> function= x-> {
List<String> list= Lists.newArrayList();
for (Student student : x) {
list.add(student.getAge());
}
return list;
};
Student student1=new Student();
student1.setAge("18");
Student student2 =new Student();
student2.setAge("19");
List<Student> list=Lists.newArrayList();
Collections.addAll(list, student1, student2);
List<String> apply = function.apply(list);
System.err.println(JSON.toJSONString(apply));
}
public static void main(String[] args) {
Function<List<Student>,List<String>> function= x->x.stream().map(y-> y.getAge()).collect(Collectors.toList());
Student student1=new Student();
student1.setAge("18");
Student student2 =new Student();
student2.setAge("19");
List<Student> list=Lists.newArrayList();
Collections.addAll(list, student1, student2);
List<String> apply = function.apply(list);
System.err.println(JSON.toJSONString(apply));
}
很酷很优雅有没有 >< 下面将会为大家介绍Stream流;
概述:当我们使用Function定义函数时,不但可以使用Function,还可以使用BiFunction以及BiConsumer等接口。
常用函数式接口表如下:
接口 | 描述 |
---|---|
Function |
接受一个输入参数,返回一个结果 |
Supplier | 无参数,返回一个结果 |
Consumer | 接受一个输入参数,并且不返回任何结果 |
BiFunction |
接受两个输入参数的方法,并且返回一个结果 |
BiConsumer |
接受两个输入参数的操作,并且不返回任何结果 |
Stream stream=Stream.empty();
List<String> list = Arrays.asList("a", "b", "c", "d");
Stream listStream = list.stream(); //获取串行的Stream对象
Stream parallelListStream = list.parallelStream(); //获取并行的Stream对象
Stream s= Stream.of("test");
Stream s1 =Stream.of("a","b","c","d");
of 内可以接受多种类型,比如数组也可以接收,代码如下:
int [] a=new int[]{10,11};
Stream<int[]> a1 = Stream.of(a);
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f);
public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
IntStream.range(0,100); //不包含最后一个数
IntStream.rangeClosed(0,99); //包含最后一个数
概述:Stream对象提供多个非常有用的方法,这些方法可以分成两类:
通过调用中间操作的方法,我们可以将Stream中的数据进行一系列的操作,如过滤,跳过,求最大值,最小值,排序,转换等一系列的过滤或转换或聚类等;
其清单如下所示,方法的具体说明及示例等请见后面:
中间操作如表:
方法 | 说明 |
---|---|
sequential | 返回一个相等的串行的Stream对象,如果原Stream对象已经是串行就可能会返回原对象 |
parallel | 返回一个相等的并行的Stream对象,如果原Stream对象已经是并行的就会返回原对象 |
unordered | 返回一个不关心顺序的Stream对象,如果原对象已经是这类型的对象就会返回原对象 |
onClose | 返回一个相等的Steam对象,同时新的Stream对象在执行Close方法时会调用传入的Runnable对象 |
close | 关闭Stream对象 |
filter | 元素过滤:对Stream对象按指定的Predicate进行过滤,返回的Stream对象中仅包含未被过滤的元素 |
map | 元素一对一转换:使用传入的Function对象对Stream中的所有元素进行处理,返回的Stream对象中的元素为原元素处理后的 |
mapToInt | 元素一对一转换:将原Stream中的使用传入的IntFunction加工后返回一个IntStream对象 |
flatMap | 元素一对多转换:对原Stream中的所有元素进行操作,每个元素会有一个或者多个结果,然后将返回的所有元素组合成一个统一的Stream并返回; |
distinct | 去重:返回一个去重后的Stream对象 |
sorted | 排序:返回排序后的Stream对象 |
peek | 使用传入的Consumer对象对所有元素进行消费后,返回一个新的包含所有原来元素的Stream对象 |
limit | 获取有限个元素组成新的Stream对象返回 |
skip | 抛弃前指定个元素后使用剩下的元素组成新的Stream返回 |
takeWhile | 如果Stream是有序的(Ordered),那么返回最长命中序列(符合传入的Predicate的最长命中序列)组成的Stream;如果是无序的,那么返回的是所有符合传入的Predicate的元素序列组成的Stream。 |
dropWhile | 与takeWhile相反,如果是有序的,返回除最长命中序列外的所有元素组成的Stream;如果是无序的,返回所有未命中的元素组成的Stream。 |
终端操作如表:
方法 | 说明 |
---|---|
iterator | 返回Stream中所有对象的迭代器; |
spliterator | 返回对所有对象进行的spliterator对象 |
forEach | 对所有元素进行迭代处理,无返回值 |
forEachOrdered | 按Stream的Encounter所决定的序列进行迭代处理,无返回值 |
toArray | 返回所有元素的数组 |
reduce | 使用一个初始化的值,与Stream中的元素一一做传入的二合运算后返回最终的值。每与一个元素做运算后的结果,再与下一个元素做运算。它不保证会按序列执行整个过程。 |
collect | 根据传入参数做相关汇聚计算 |
min | 返回所有元素中最小值的Optional对象;如果Stream中无任何元素,那么返回的Optional对象为Empty |
max | 与Min相反 |
count | 所有元素个数 |
anyMatch | 只要其中有一个元素满足传入的Predicate时返回True,否则返回False |
allMatch | 所有元素均满足传入的Predicate时返回True,否则False |
noneMatch | 所有元素均不满足传入的Predicate时返回True,否则False |
findFirst | 返回第一个元素的Optioanl对象;如果无元素返回的是空的Optional; 如果Stream是无序的,那么任何元素都可能被返回。 |
findAny | 返回任意一个元素的Optional对象,如果无元素返回的是空的Optioanl。 |
|isParallel| 判断是否当前Stream对象是并行的|
Filter:
Stream<T> filter(Predicate<? super T> predicate);
/**
* @program:
* @description:
* @author: anyu
* @create: 2020-03-09
*/
@Data
public class Student implements Serializable {
private String name;
private String age;
}
public static void main(String[] args) {
Student student1=new Student();
student1.setAge("18");
Student student2 =new Student();
student2.setAge("19");
List<Student> list=Lists.newArrayList();
Collections.addAll(list, student1, student2);
long count = list.stream().filter(x -> x.getAge().equals("18")).count(); // 打印出list集合内年龄等于18岁的数据总数
System.err.println(count);
}
这里定义了一个有年龄对象的集合,使用filter能够过滤出年龄等于18岁的个数。实际上不使用.count,用其他的终结语句还能转换为另一个集合,或者找出等于18岁的这个数据的具体对象;
map:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
public static void main(String[] args) {
Student student1=new Student();
student1.setAge("18");
Student student2 =new Student();
student2.setAge("19");
List<Student> list=Lists.newArrayList();
Collections.addAll(list, student1, student2);
List<String> collect = list.stream().map(x -> x.getAge() + "岁").collect(Collectors.toList());
System.err.println(JSON.toJSONString(collect));
}
这里的 x-> x… 里面的x可以用其他字母代替,它只是一个替代名称,不具备实际意义,表示集合内的这一个元素;
通过map集合,我们可以将每个值进行映射,带了一个岁的后缀,当使用collect终结stream时,就转换成了一个新的stream流了。代码运行后控制台打印结果如下:
flatMap:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
public static void main(String[] args) {
Student student1 = new Student();
student1.setAge("18");
student1.setName("张三啊");
Student student2 = new Student();
student2.setAge("19");
student2.setName("李四啊");
List<Student> list = Lists.newArrayList();
Collections.addAll(list, student1, student2);
List<String> collect = list.stream().flatMap(x->Stream.of(x.getName())).collect(Collectors.toList());
System.err.println(JSON.toJSONString(collect));
}
> 这里的flatMap可以将每个元素再Stream流化,能够进行更细粒度的操作。比起Map的一对一,它的功能更为强大,具体的区别可百度。
takeWhile:
它与Filter有点类似,不同之处在于当Stream是有序的,它遇到不符合条件的就会停止,而Filter会继续筛选。
default Stream<T> takeWhile(Predicate<? super T> predicate)
Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa", "taaa");
//以下结果将打印: "test", "t1", "t2", "teeeee",最后的那个taaa不会进行打印
s.takeWhile(n -> n.contains("t")).forEach(System.out::println);
dropWhile:
default Stream<T> dropWhile(Predicate<? super T> predicate)
forEach
public static void main(String[] args) {
Student student1 = new Student();
student1.setAge("18");
student1.setName("张三啊");
Student student2 = new Student();
student2.setAge("19");
student2.setName("李四啊");
Student student3=new Student();
student3.setAge("20");
student3.setName("王五啊");
List<Student> list = Lists.newArrayList();
Collections.addAll(list, student1, student2,student3);
list.forEach(x-> System.err.println(x.getAge()));
}
sorted和distinct:
Integer[] arr = new Integer[]{5, 1, 2, 1, 3, 1, 2, 4}; // 千万不要用int
Stream.of(arr).sorted().forEach(System.out::println);
Stream.of(arr).distinct().forEach(System.out::println);
Stream.of(arr).distinct().sorted().forEach(System.out::println);
collect:
public static void main(String[] args) {
Student student1 = new Student();
student1.setAge("18");
student1.setName("张三啊");
Student student2 = new Student();
student2.setAge("19");
student2.setName("李四啊");
Student student3=new Student();
student3.setAge("20");
student3.setName("王五啊");
List<Student> list = Lists.newArrayList();
Collections.addAll(list, student1, student2,student3);
List<Student> collect = list.stream().filter(x -> x.getAge().equals("19")).collect(Collectors.toList());
System.err.println(JSON.toJSONString(collect));
}