-
lambda表达式(lambda表达式的简化、函数式接口)
2020-07-12 21:29:55 Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更简洁的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。 JDK 也提供了大量的内置函数式接口供我们使用,...lambda表达式 一、Lambda简介
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更简洁的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。二、对接口的要求
虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,但不是说接口中只能有一个方法。
三、lambda的基础语法
语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 。
如果只有一个参数时()可以省略,方法体只有返回值或只有一行代码时{}可以省略,例如消费型与供给型接口所示。四、四大函数型接口lambda表达式示例
- 查看非lambda代码的四大函数型接口点击此处
- 以下为lambda代码简化后的函数式接口示例
public static void main(String[] args) { //消费型 //consumertest(10000,(i) -> {System.out.println("消费了:"+i+"元");}); consumertest(10000,i -> System.out.println("消费了:"+i+"元")); //供给型 //System.out.println(getValue(() -> { // return (int) (Math.random() * 10) + 1; //})); System.out.println(getValue(()->(int)(Math.random()*10)+1)); //方法型 System.out.println(functiontest(" 哈哈 ", (str) -> str.trim())); //断言型 List<Integer> list = new ArrayList<>(); list.add(300); list.add(3); list.add(30); list.add(3333); list.add(30033); List<Integer> list1 = predicatetest(list,(li)->li>100); System.out.println(list1); } //消费型接口 public static void consumertest(Integer i, Consumer<Integer> com){ com.accept(i); } //供给型 public static Integer getValue(Supplier<Integer> s){ return s.get(); } //方法型 public static String functiontest(String str,Function<String,String> fun){ return fun.apply(str); } //断言型 public static List<Integer> predicatetest(List<Integer> list, Predicate<Integer> pre){ List<Integer> newlist = new ArrayList<>(); for (Integer i : list) { if (pre.test(i)){ newlist.add(i); } } return newlist; }
五、lambda表达式的简化
语法:
方法引用: 简化Lambda–>Lambda表达式特殊的一种表现形式当lambda体中的实现是通过调用另外一个方法实现的时候,这个方法的参数列表和返回值类型与抽象方法的参数列表与返回值一致的时候,就可以使用方法引用进行代替。
方法引用分为:
引用::成员方法名
类名::静态方法名
类名::成员方法名public static void main(String[] args) { List<String> list = new ArrayList(); list.add("123"); list.add("12"); list.add("1"); //引用::成员方法名 //遍历容器,打印里面的每一个数据 list.forEach((i)->{ System.out.println(i); }); //Lambda体实现的功能就是打印参数-->可以通过方法引用来代替 //通过PrintStream这个类型的对象调用println实现的, //Lambda的抽象方法的参数作为println方法的参数,两者都没有返回值 PrintStream ps = System.out; list.forEach(ps::println); list.forEach(System.out::println); //类名::静态方法名 //比较2个double参数,返回最大的一个 //2个参数 1个返回值 BiFunction //求最大值r //lambda表达式 //BiFunction<Double,Double,Double> fun = (d1, d2)->Math.max(d1,d2); //lambda的简化 BiFunction<Double,Double,Double> fun = Math::max; System.out.println(fun.apply(100.0, 200.0)); //比较两个int类型的参数 Comparator<Integer> com = (i1, i2)->Integer.compare(i1,i2); //lambda简化 com = Integer::compare; System.out.println(com.compare(12, 13)); //比较两个字符串是否相等 BiPredicate<String,String> pre = (s1,s2)-> s1.equals(s2); //1)lambda是否是通过引用另外一个方法实现的->是 //2)返回值匹配 //3)抽象方法第一个参数s1作为内部引用另一个方法的对象存在, // 如果抽象方法存在多个参数,第二个参数开始匹配内部引用的方法的参数列表-->方法引用 pre = String::equals; System.out.println(pre.test("zhangsan", "zhangsan")); //类名::成员方法名 //获取bean中的属性 Employee e = new Employee("ggh",12); Supplier<String> sup = ()->e.getName(); sup = e::getName; //对象固定 System.out.println("名字为:"+sup.get()); Function<Employee,String> func = (em)-> em.getName(); func = Employee::getName; //对象可变 System.out.println(func.apply(e)); }
-
Java学习day041 lambda表达式(为什么引入lambda表达式、lambda表达式的语法、函数式接口、方法引用)
2020-04-09 18:22:55使用的教材是java核心技术卷1,我将跟着这本书的章节同时配合视频资源来...lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。先观察一下我们在Java中的哪些地方用过这种代码块。 在之前我们已经了解了...使用的教材是java核心技术卷1,我将跟着这本书的章节同时配合视频资源来进行学习基础java知识。
day041 lambda表达式(为什么引入lambda表达式、lambda表达式的语法、函数式接口、方法引用)
1.为什么引入lambda表达式
lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。先观察一下我们在Java中的哪些地方用过这种代码块。
在之前我们已经了解了如何按指定时间间隔完成工作。将这个工作放在一个ActionListener的actionPerformed方法中:
class Worker implements ActionListener { public void actionPerformed(ActionEvent event) { //do some work } }
想要反复执行这个代码时,可以构造Worker类的一个实例。然后把这个实例提交到一个Timer对象。这里的重点是actionPerformed方法包含希望以后执行的代码。
或者可以考虑如何用一个定制比较器完成排序。如果想按长度而不是默认的字典顺序对字符串排序,可以向sort方法传人一个Comparator对象:
class LengthComparator implements Comparator { public int compare(String first,String second) { return first.length()-second.length(); } } ... Arrays.sort(strings,new LengthComparator());
compare方法不是立即调用。实际上,在数组完成排序之前,sort方法会一直调用compare方法,只要元素的顺序不正确就会重新排列元素。将比较元素所需的代码段放在sort方法中,这个代码将与其余的排序逻辑集成(你可能并不打算重新实现其余的这部分逻辑)。
这两个例子有一些共同点,都是将一个代码块传递到某个对象(一个定时器,或者一个sort方法)。这个代码块会在将来某个时间调用。到目前为止,在Java中传递一个代码段并不容易,不能直接传递代码段。Java是一种面向对象语言,所以必须构造一个对象,这个对象的类需要有一个方法能包含所需的代码。
在其他语言中,可以直接处理代码块。Java设计者很长时间以来一直拒绝增加这个特性。毕竟,Java的强大之处就在于其简单性和一致性。如果只要一个特性能够让代码稍简洁一些,就把这个特性增加到语言中,这个语言很快就会变得一团糟,无法管理。不过,在另外那些语言中,并不只是创建线程或注册按钮点击事件处理器更容易;它们的大部分API都更简单、更一致而且更强大。在Java中,也可以编写类似的AP丨利用类对象实现特定的功能,不过这种API使用可能很不方便。
就现在来说,问题已经不是是否增强Java来支持函数式编程,而是要如何做到这一点。设计者们做了多年的尝试,终于找到一种适合Java的设计。
2.lambda表达式的语法
再来考虑上面讨论的排序例子。我们传人代码来检查一个字符串是否比另一个字符串短。这里要计算:
first.length()-second.length()
first和second是什么?它们都是字符串。Java是一种强类型语言,所以我们还要指定它们的类型:
(String first,String second)->first.length()-second.length()
这就是你看到的第一个表达式。lambda表达式就是一个代码块,以及必须传人代码的变量规范。
为什么起这个名字呢?很多年前,那时还没有计算机,逻辑学家Alonzo Church想要形式化地表示能有效计算的数学函数。(奇怪的是,有些函数已经知道是存在的,但是没有人知道该如何计算这些函数的值。)他使用了希腊字母lambda(λ)来标记参数如果他知道JavaAPI,可能就会写为
λfirst.λsecond.first.length()-second.length()
前面已经见过Java中的一种lambda表达式形式:参数,箭头(->)以及一个表达式。如果代码要完成的计算无法放在一个表达式中,就可以像写方法一样,把这些代码放在{}中,并包含显式的return语句。例如:
(String first,String second)-> { if(first.length() < second.length()) return -1 ; else if (first.length() > second.length()) return 1; else return 0; }
即使lambda表达式没有参数,仍然要提供空括号,就像无参数方法一样:
0->{ for(int i=100;i>=0;i--) System.out.println(i);}
如果可以推导出一个lambda表达式的参数类型,则可以忽略其类型。例如:
Comparator<String> comp =(first,second)//Same as (String first,String second) ->first.length()-second.length();
在这里,编译器可以推导出first和second必然是字符串,因为这个lambda表达式将赋给一个字符串比较器。
如果方法只有一参数,而且这个参数的类型可以推导得出,那么甚至还可以省略小括号:
ActionListener listener = event-> System.out.println("The time is"+new Date()"); //Instead of(event)->...or(ActionEvent event)->...
无需指定lambda表达式的返回类型。lambda表达式的返回类型总是会由上下文推导得出。例如,下面的表达式
(String first,String second)->first.length()-second.length()
可以在需要im类型结果的上下文中使用。
如果一个lambda表达式只在某些分支返回一个值,而在另外一些分支不返回值,这是不合法的。例如,
(int x)->{if(x>=0) return 1;}
就不合法。
下面的程序显示了如何在一个比较器和一个动作监听器中使用lambda表达式。
/** *@author zzehao */ import java.util.*; import javax.swing.*; import javax.swing.Timer; public class LambdaTest { public static void main(String[] args) { String[] planets = new String[] { "Mercury", "Venus", "Earth","Mars", "Jupiter", "Saturn", "Uranus", "Neptune" }; System.out.println(Arrays.toString(planets)); System.out.println("Sorted in dictionary order:"); Arrays.sort(planets); System.out.println(Arrays.toString(planets)); System.out.println("Sorted by length:"); Arrays.sort(planets, (first, second) -> first.length() - second.length()); System.out.println(Arrays.toString(planets)); Timer t = new Timer(1000, event -> System.out.println("The time is "+ new Date())); t.start(); //keep program running until user selects "0k" JOptionPane.showMessageDialog(null, "Quit program?"); System.exit(0); } }
运行的结果是:
3.函数式接口
前面已经讨论过,Java中已经有很多封装代码块的接口,如ActionListener或Comparator。lambda表达式与这些接口是兼容的。
对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式。这种接口称为函数式接口(functional interface)。
为了展示如何转换为函数式接口,下面考虑Arrays.sort方法。它的第二个参数需要一个Comparator实例,Comparator就是只有一个方法的接口,所以可以提供一个lambda表达式:
Arrays.sort(words,(first,second)->first.length()-second.length());
在底层,Arrays.sort方法会接收实现了Comparator的某个类的对象。在这个对象上调用compare方法会执行这个lambda表达式的体。这些对象和类的管理完全取决于具体实现,与使用传统的内联类相比,这样可能要高效得多。最好把lambda表达式看作是一个函数,而不是一个对象,另外要接受lambda表达式可以传递到函数式接口。
lambda表达式可以转换为接口,这一点让lambda表达式很有吸引力。具体的语法很简短。下面再来看一个例子:
Timer t = new Timer(1000,event-> { System.out.println("At the tone,the time is"+new Date()); Toolkit.getDefaultToolkit().beep(); });
与使用实现了ActionListener接口的类相比,这个代码可读性要好得多。
实际上,在Java中,对lambda表达式所能做的也只是能转换为函数式接口。在其他支持函数字面量的程序设计语言中,可以声明函数类型(如(String,String)->int)、声明这些类型的变量,还可以使用变量保存函数表达式。不过,Java设计者还是决定保持我们熟悉的接口概念,没有为Java语言增加函数类型。
甚至不能把lambda表达式赋给类型为Object的变量,Object不是一个函数式接口。
JavaAPI在java.util.fimction包中定义了很多非常通用的函数式接口。其中一个接口BiFunction<T,U,R>描述了参数类型为T和U而且返回类型为R的函数。可以把我们的字符串比较lambda表达式保存在这个类型的变量中:
BiFunction<String,String,Integer>comp =(first,second)->first.length()-second.length();
不过,这对于排序并没有帮助。没有哪个Arrays.sort方法想要接收一个BiFunction。如果你之前用过某种函数式程序设计语言,可能会发现这很奇怪。不过,对于Java程序员而言,这非常自然。类似Comparator的接口往往有一个特定的用途,而不只是提供一个有指定参数和返回类型的方法。Java SE 8沿袭了这种思路。想要用lambda表达式做某些处理,还是要谨记表达式的用途,为它建立一个特定的函数式接口。
java.util.function包中有一个尤其有用的接口Predicate:
public interface Predicate<T> { boolean test(T t); //Additional default and static methods }
ArrayList类有一个removelf方法,它的参数就是一个Predicate。这个接口专门用来传递lambda表达式。例如,下面的语句将从一个数组列表删除所有null值:
list.removelf(e->e==null);
4.方法引用
有时,可能已经有现成的方法可以完成你想要传递到其他代码的某个动作。例如,假设你希望只要出现一个定时器事件就打印这个事件对象。当然,为此也可以调用:
Timer t = new Timer(1000,event->System.out.println(event));
但是,如果直接把println方法传递到Timer构造器就更好了。具体做法如下:
Timer t = new Timer(1000,System.out::println);
表达式System.out::println是一个方法引用(method reference),它等价于lambda表达式x一>System.out.println(x)。
再来看一个例子,假设你想对字符串排序,而不考虑字母的大小写。可以传递以下方法表达式:
Arrays.sort(strings,String::compareToIgnoreCase)
从这些例子可以看出,要用::操作符分隔方法名与对象或类名。主要有3种情况:
•object::instanceMethod
•Class::staticMethod
•Class::instanceMethod
在前2种情况中,方法引用等价于提供方法参数的lambda表达式。前面已经提到,System.out::println等价于x->System.out.println(x)。类似地,Math::pow等价于(x,y)->Math.pow(x,y)。
对于第3种情况,第1个参数会成为方法的目标。例如,String::compareToIgnoreCase等同于(x,y)->x.compareToIgnoreCase(y)。
如果有多个同名的重栽方法,编译器就会尝试从上下文中找出你指的那一个方法。例如,Math.max方法有两个版本,一个用于整数,另一个用于double值。选择哪一个版本取决于Math::max转换为哪个函数式接口的方法参数。类似于lambda表达式,方法引用不能独立存在,总是会转换为函数式接口的实例。
可以在方法引用中使用this参数。例如,this::equals等同于x->this.equals(x)。使用super也是合法的。下面的方法表达式
super::instanceMethod
使用this作为目标,会调用给定方法的超类版本。
为了展示这一点,下面给出一个假想的例子:
class Greeter { public void greet() { System.out.println("Hello,world!"); } } class TimedCreeter extends Greeter { public void greet() { Timer t = new Timer(1000,super::greet); t.start(); } }
TimedGreeter.greet方法开始执行时,会构造一个Timer,它会在每次定时器滴答时执行super::greet方法。这个方法会调用超类的greet方法。
-
Lambda表达式的运用以及函数式接口
2020-11-09 14:06:04lambda表达式所运用的场景实际上是基于函数式接口。 1、无参数,无返回值 @Test public void test1() { new Thread(()->{ System.out.println(Thread.currentThread().getName() + "启动"); },"A")....Lambda表达式的运用
lambda表达式所运用的场景实际上是基于函数式接口。
1、无参数,无返回值
@Test public void test1() { new Thread(()->{ System.out.println(Thread.currentThread().getName() + "启动"); },"A").start(); }
如果在当前类中有一个变量,在lambda中想要获取该变量的值,需要将该变量声明为final
例如:@Test public void test1() { final int i = 1; new Thread(()->{ System.out.println(Thread.currentThread().getName() + i); },"A").start(); }
但是Java8之后,final可以省略。
2、有参数无返回值
典型的使用Consumer类型的接口@Test public void test2() { //如果只有一个参数,该小括号可以不写即 x -> System.out.println(x) Consumer<String> consumer = (x) -> System.out.println(x); consumer.accept("hello word"); } 输出的结果为:hello word
3、有两个参数和返回值
@Test public void test3() { Comparator<Integer> com = (x,y) -> { System.out.println("比较两个数的大小"); return Integer.compare(x, y); }; System.out.println(com.compare(5, 2)); } 如果有两个或多个语句时,lambda后面一定要加上{ }
如果只要返回指定结果,还可以这样写
@Test public void test4() { Comparator<Integer> com = (x,y) -> Integer.compare(x, y); System.out.println(com.compare(5, 2)); }
4种函数式接口
函数式接口是一个接口,只不过该接口中只有一个抽象的方法,使用注解@FunctionInterface修饰
1、Function接口函数型接口 Function<T,R>{ R apply(T t); }
其中T是传入的参数,R是返回的参数
@Test public void test5() { Function<Integer, String> fun = (x) -> Integer.toString(x); System.out.println(fun.apply(3)); } 传入一个Integer的3,返回一个String类型的"3"
2、Consumer接口
消费类型的接口 Consumer<T> { void accept(T t); }
其中T是传入的参数类型
@Test public void test2() { Consumer<String> consumer = x -> System.out.println(x); consumer.accept("hello word"); }
3、Supplier接口
供给型接口 Supplier<T>{ T get(); }
其中T是返回的参数类型
@Test public void test6() { Supplier<String> sup = () -> "hello"; System.out.println(sup.get()); } 返回一个String的值
4、Predict接口
断言型接口 Predict<T>{ boolean test(T t); }
@Test public void test7() { Predicate<String> pre = (x) -> x.equals("a"); System.out.println(pre.test("a"));//true }
-
java8 Lambda表达式的应用(函数式接口、lambda表达式,方法引用及Stream API)
2020-03-20 13:28:48之前写了一篇博客简单介绍了一下java 8发布新增的一些特性功能,java 8在2014年发布,距今也不少年了,但是lambda表达式使用并不熟悉,现在一边...使用Lambda表达式可以替代只有一个抽象方法的接口(函数式接口)实...之前写了一篇博客简单介绍了一下java 8发布新增的一些特性功能,java 8在2014年发布,距今也不少年了,但是lambda表达式使用并不熟练,现在一边学习,一边记录一下。
目录
一、Lambda表达式
是java8新增的最重要的新功能之一。使用Lambda表达式是 Java8 中最重要的新功能之一。使用 Lambda 表达式可以替代只有一个抽象方法的接口(函数式接口)实现,告别匿名内部类,代码看起来更简洁易懂。Lambda表达式同时还提升了对集合、框架的迭代、遍历、过滤数据的操作。
Lambda表达式特点
1:函数式编程
2:参数类型自动推断
3:代码量少,简洁
学习Lambda表达式需要熟悉java泛型,平时有意识地使用lambda表达式,学习java中固有的一些函数式接口,多用stream API。
函数式接口(函数式接口都可以使用Lambda表达式):只有一个抽象方法的接口
注意:如果自定义的接口使用Object类中的方法作为抽象方法,则该接口不是函数式接口。例如在接口中定义一个抽象方法:public int hashCode();
在java8中可以用@FuctionalInterface注解来验证函数式接口。
常用的函数式接口:
Runnable接口:
@FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
Runnable接口是函数式接口因此可以使用lambda表达式。
Callable接口:
@FunctionalInterface public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
Comparator接口:
还有jdk中其他常用的函数式接口:
在java.util.function包下的类几乎都是函数式接口:
其中里面常用的接口有
Supplier接口:可以表示为一个输出(获取一个结果),返回T类型
@FunctionalInterface public interface Supplier<T> { /** * Gets a result. * * @return a result */ T get(); }
Consumer:可以表示为一个输入(操作一个输入的参数)
@FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t); /** * Returns a composed {@code Consumer} that performs, in sequence, this * operation followed by the {@code after} operation. If performing either * operation throws an exception, it is relayed to the caller of the * composed operation. If performing this operation throws an exception, * the {@code after} operation will not be performed. * * @param after the operation to perform after this operation * @return a composed {@code Consumer} that performs in sequence this * operation followed by the {@code after} operation * @throws NullPointerException if {@code after} is null */ default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
BiConsumer:可以表示为两个输入(有两个输入参数T和U)
@FunctionalInterface public interface BiConsumer<T, U> { /** * Performs this operation on the given arguments. * * @param t the first input argument * @param u the second input argument */ void accept(T t, U u); /** * Returns a composed {@code BiConsumer} that performs, in sequence, this * operation followed by the {@code after} operation. If performing either * operation throws an exception, it is relayed to the caller of the * composed operation. If performing this operation throws an exception, * the {@code after} operation will not be performed. * * @param after the operation to perform after this operation * @return a composed {@code BiConsumer} that performs in sequence this * operation followed by the {@code after} operation * @throws NullPointerException if {@code after} is null */ default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) { Objects.requireNonNull(after); return (l, r) -> { accept(l, r); after.accept(l, r); }; } }
Function接口:可以表示为通过一个输入参数,获取一个输出(即一个输入,一个输出),传入一个T类型,获取一个R类型数据
@FunctionalInterface public interface Function<T, R> { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t); /** * Returns a composed function that first applies the {@code before} * function to its input, and then applies this function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of input to the {@code before} function, and to the * composed function * @param before the function to apply before this function is applied * @return a composed function that first applies the {@code before} * function and then applies this function * @throws NullPointerException if before is null * * @see #andThen(Function) */ default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } /** * Returns a composed function that first applies this function to * its input, and then applies the {@code after} function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of output of the {@code after} function, and of the * composed function * @param after the function to apply after this function is applied * @return a composed function that first applies this function and then * applies the {@code after} function * @throws NullPointerException if after is null * * @see #compose(Function) */ default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } /** * Returns a function that always returns its input argument. * * @param <T> the type of the input and output objects to the function * @return a function that always returns its input argument */ static <T> Function<T, T> identity() { return t -> t; } }
UnaryOperator接口:该接口继承了Funcition接口,可以表示为输入一个参数,获取一个输出数据(不同的是,输入和输出的类型相同了,都是T类型)
@FunctionalInterface public interface UnaryOperator<T> extends Function<T, T> { /** * Returns a unary operator that always returns its input argument. * * @param <T> the type of the input and output of the operator * @return a unary operator that always returns its input argument */ static <T> UnaryOperator<T> identity() { return t -> t; } }
BiFunction接口:表示两个输入(输入类型不同),一个输出(输入和输出类型都不同)
@FunctionalInterface public interface BiFunction<T, U, R> { /** * Applies this function to the given arguments. * * @param t the first function argument * @param u the second function argument * @return the function result */ R apply(T t, U u); /** * Returns a composed function that first applies this function to * its input, and then applies the {@code after} function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of output of the {@code after} function, and of the * composed function * @param after the function to apply after this function is applied * @return a composed function that first applies this function and then * applies the {@code after} function * @throws NullPointerException if after is null */ default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t, U u) -> after.apply(apply(t, u)); } }
BinaryOperator接口:传入两个参数类型(输入),获取返回一个结果(输出),输入和输出类型相同
@FunctionalInterface public interface BinaryOperator<T> extends BiFunction<T,T,T> { /** * Returns a {@link BinaryOperator} which returns the lesser of two elements * according to the specified {@code Comparator}. * * @param <T> the type of the input arguments of the comparator * @param comparator a {@code Comparator} for comparing the two values * @return a {@code BinaryOperator} which returns the lesser of its operands, * according to the supplied {@code Comparator} * @throws NullPointerException if the argument is null */ public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) { Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) <= 0 ? a : b; } /** * Returns a {@link BinaryOperator} which returns the greater of two elements * according to the specified {@code Comparator}. * * @param <T> the type of the input arguments of the comparator * @param comparator a {@code Comparator} for comparing the two values * @return a {@code BinaryOperator} which returns the greater of its operands, * according to the supplied {@code Comparator} * @throws NullPointerException if the argument is null */ public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) { Objects.requireNonNull(comparator); return (a, b) -> comparator.compare(a, b) >= 0 ? a : b; } }
其他的在这个包下还有一些接口名称带有类型的,可以顾名思义它的作用,如DoubleConsumer接口,则表示输入的参数类型为double类型,其他的类似,可以参考源码理解
@FunctionalInterface public interface DoubleConsumer { /** * Performs this operation on the given argument. * * @param value the input argument */ void accept(double value); /** * Returns a composed {@code DoubleConsumer} that performs, in sequence, this * operation followed by the {@code after} operation. If performing either * operation throws an exception, it is relayed to the caller of the * composed operation. If performing this operation throws an exception, * the {@code after} operation will not be performed. * * @param after the operation to perform after this operation * @return a composed {@code DoubleConsumer} that performs in sequence this * operation followed by the {@code after} operation * @throws NullPointerException if {@code after} is null */ default DoubleConsumer andThen(DoubleConsumer after) { Objects.requireNonNull(after); return (double t) -> { accept(t); after.accept(t); }; } }
labmda表达式语法,只有函数式接口可以使用lambda表达式(正因为函数式接口中只有一个抽象方法,所以lambda表达式不用写方法名,可以推断出来),lambda表达式是对象,是一个函数式接口的实例。
lambda表达式语法:LambdaParameter -> LambdaBody
例如 Runnable r=() -> System.out.println();
args -> expr
对应
(object... args)-> {函数式接口抽象方法的实现逻辑}
左边括号里边的参数个数,根据函数式接口里面抽象方法的参数个数决定。
当只有一个参数的时候,()可以省略,当方法体逻辑很简单的时候,{}和return 也可以省略
Lambda表达式示例:
() -> {} // 无参,无返回值
() -> { System.out.println(1); } // 无参,无返回值
() -> System.out.println(1) // 无参,无返回值(上面的简写)
() -> { return 100; } // 无参,有返回值
() -> 100 // 无参,有返回值(上面的简写)
() -> null // 无参,有返回值(返回null)
(int x) -> { return x+1; } // 单个参数(可以写出参数类型,不写类型,会自动推断,因此可以写成下边的形式)有返回值
(int x) -> x+1 // 单个参数,有返回值(上面的简写,省略了return)
(x) -> x+1 // 单个参数,有返回值(不指定参数类型,因为可以根据接口中的抽象方法参数类型推断出来,多个参数必须用括号)
x -> x+1 // 单个参数,有返回值(不指定参数类型)
注意事项:
多个参数类型时,要加上(),并且参数类型不能只省略一部分,
如:(x,int y)-> x+y
参数类型不能使用final修饰符,
如:(x, final y)-> x+y
不能将lambda表达式赋给一个非函数式接口
如:Object obj=()-> "hello"
可以将lambda表达式强转为一个有返回值的函数式接口
Object obj = (Supplier<?>)() -> "hello"
不需要也不允许使用throws语句来声明它可能会抛出的异常
实例演示:
package com.xiaomifeng1010.rbacboot.common.util; import org.apache.commons.lang3.StringUtils; import java.io.Closeable; import java.util.Observer; import java.util.concurrent.Callable; import java.util.function.*; /** * @author xiaomifeng1010 * @version 1.0 * @date: 2020/3/19 18:52 */ public class LambdaExpression { public static void main(String[] args) throws Exception { // 无参无返回值接口Runnable,如果不使用lambda表达式,则是下边这样的 Runnable runnable2=new Runnable() { @Override public void run() { } }; // 无参无返回值接口Runnable,使用lambda表达式,效果等同上边的 Runnable runnable=() -> {}; // 如果方法体中只有一行逻辑实现的语句,大括号可以省略 Runnable r=() -> System.out.println("无参无返回值的接口Runnable"); Closeable closeable = () -> System.out.println("closeable也是无参无返回值"); closeable.close(); // 无参有返回值接口Callable,如果不使用lambda表达式 Callable<String> callable=new Callable<String> () { @Override public String call() { return "call方法"; } }; //无参有返回值接口Callable,使用lambda表达式,效果等同上边的 Callable<String> callable2 = () -> { return "call方法"; }; // 因为方法体中只有一句返回语句,可以省略大括号和return Callable callable3 = () -> "call方法"; System.out.println(callable.call()); System.out.println(callable2.call()); System.out.println(callable3.call()); // 有参无返回值自定义接口UserMapper,不使用lambda表达式时 UserMapper userMapper=new UserMapper() { @Override public void insert(User user) { System.out.println("插入一条数据"); } }; // 有参无返回值,使用lambda表达式时 UserMapper userMapper2=(User user) -> System.out.println("插入一条数据"); //只有一个参数时,参数括号也可以省略,类型也可以省略(会自动推断) UserMapper userMapper3= user -> System.out.println("插入一条数据"); // 有参有返回值的自定义接口OrderMapper,不使用lambda表达式 OrderMapper orderMapper=new OrderMapper() { @Override public int insert(Order order) { return 0; } }; // 有参有返回值的自定义接口OrderMapper,使用lambda表达式(只有一个参数可省略括号和类型,方法体简单,只有一行可省略 // 大括号和return OrderMapper orderMapper2=order -> 0; System.out.println("orderMapper插入了"+orderMapper.insert(new Order())+"行数据"); System.out.println("orderMapper2插入了"+orderMapper2.insert(new Order())+"行数据"); // Function接口的使用示例,输入a是Integer类型,返回类型是String类型 Function<Integer,String> function=a -> "function接口返回值"; // Function接口的使用示例,输入a是Integer类型,返回类型是String类型,由方法获取String.valueOf(b)返回String类型 Function<Integer,String> function2 = b -> String.valueOf(b); System.out.println("function接口传入10,返回:"+function.apply(10)); System.out.println("function2接口传入10,返回:"+function.apply(10)); // BiFunction输入两个不同类型参数(当然也可以相同类型参数),输出一个类型数据 BiFunction<Integer,Double,String> biFunction=(a,b) -> String.valueOf(a)+String.valueOf(b); System.out.println("biFunction传入a,b值后:"+biFunction.apply(10,20.56)); // BinaryOperator输入的两个相同类型参数,输出相同类型的数据类型 BinaryOperator<Integer> binaryOperator=(a,b) -> 10; System.out.println("binaryOperator输入a和b之后:"+binaryOperator.apply(5,6)); // Supplier接口获取一个输出 Supplier<String> supplier = () -> "supplier"; System.out.println("supplier不需要传入参数,就可以获取一个数据:"+supplier.get()); // Consumer输入一个类型参数,无返回值 Consumer<String> consumer = (String s) -> { System.out.println("consumer"); }; // 简写为: Consumer<String> consumer2 = s-> System.out.println("consumer"); // 注意函数式接口的方法中有返回值的,使用lambda表达式方法体中必须要有返回值,且返回值类型要相同,而函数式接口抽象方法 // 没有返回值的,使用lambda表达式的方法体可以使用有返回值的方法。例如Runnable接口的抽象方法run()没有返回值; // Integer.parseInt()会返回int类型 Runnable runnable4 =() -> Integer.parseInt("10"); // 但是直接写10,是不行的 // Runnable runnable1 = () -> 10; // 说明:为什么可以直接用方法,而不能直接使用int值,因为虽然Integer.parseInt("10")会返回一个int值,但是run // 方法是无返回值的,所以调用Integer.parseInt("10")可以只是用他的方法逻辑,不用返回值,但是直接写10,就是直接给了 // 一个返回值,是不可以的 } } /** * 自定义一个接口 */ @FunctionalInterface interface UserMapper{ void insert(User user); } @FunctionalInterface interface OrderMapper{ int insert(Order order); } class User{ } class Order { }
写lambda表达式:
1.看抽象方法参数;2,看返回类型
二、方法引用
方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法,方法引用提供了一种引用而不执行方法的方式,如果抽象方法的实现恰好可以使用调用另外一个方法来实现,就有可能可以使用方法引用(因为如果方法体还需要写其他逻辑,就不能直接使用方法引用)。
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)方法引用:使用操作符“::” 将方法名和对象或类的名字分隔开来。
如下四种主要使用情况:类型
语法
对应的lambda表达式
静态方法引用
类名::staticMethod
(args) -> 类名.staticMethod(args)
实例方法引用
inst::instMethod
(args) -> inst.instMethod(args)
对象方法引用
类名::instMethod
(inst,args) ->类名.instMethod(args)
构造方法引用
类名::new
(args) -> new 类名(args)
如果函数式接口的实现恰好可以通过调用一个静态方法来实现,那么就可以使用静态方法引用;
如果函数式接口的实现恰好可以通过调用一个实例的实例方法来实现,那么就可以使用实例方法引用;
抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。如果函数式接口的实现能由上面说的实例方法调用来实现的话,那么就可以使用对象方法引用;
如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现,那么就可以使用构造方法引用。
实例演示:package com.xiaomifeng1010.rbacboot.common.util; import java.util.function.*; /** * @author xiaomifeng1010 * @version 1.0 * @date: 2020/3/19 21:32 */ public class MethodReference { public static void main(String[] args) { // 静态方法引用实例 BinaryOperator<Double> binaryOperator=(a,b) -> Math.pow(a,b); // Math.pow(a,b)方法需要两个输入参数,返回一个数据,且输入参数类型和返回值类型相同,完全符合BinaryOperator // 接口特点 BinaryOperator<Double> binaryOperator2=Math::pow; // 构造方法引用 // Supplier接口的get()方法不需要输入参数,返回一个值(输出),可以用 // String类(注意这里的String类不是lang包下的类,而是com.sun.org.apache.xpath.internal.operations.String)的构造方法实现 Supplier<String> supplier=() -> new String(); Supplier<String> supplier2=String::new; // 类型也可以使用数组类型 Function<Integer,Integer[]> function6=integer -> new Integer[integer]; Function<Integer,Integer[]> function7=Integer[]::new; // 实例方法引用 MethodReference mr = new MethodReference(); Function<String,String> function3=str -> mr.toUpper(str); // 或者直接简写为(下边这种写法不用写MethodReference mr = new MethodReference() Function<String,String> function5=new MethodReference()::toUpper; // 例子2,较特殊 Function<String,String> function=str -> str.toUpperCase(); // 方法引用写法,因为toUpperCase()方法不是静态方法,但是直接使用String引用toUpperCase()方法 Function<String,String> function2=String::toUpperCase; // 对象方法引用实例 // 抽象方法的第一个参数类型刚好是实例方法的类型,抽象方法剩余的参数恰好可以当做实例方法的参数。 // 如果函数式接口的实现能由上面说的实例方法调用来实现的话,那么就可以使用对象方法引用 // lambda表达式方法体有现成方法实现时,lambda表达式完全写法(方法体中用类名.实例方法) Consumer<Too> consumer=(Too too) -> new Too().too(); Consumer<Too> consumer2=Too::too; // 接口中有两个参数的情况,BiConsumer接口接收两个输入参数,无返回值 BiConsumer<Too,String> biConsumer=(Too too2,String str) -> new Too().top(str); BiConsumer<Too,String> biConsumer2=Too::top; // 接口中三个参数类型 BiFunction<Too,String,String> biFunction=(Too too2,String str) -> new Too().tos(str); BiFunction<Too,String,String> biFunction2=Too::tos; // 说明:如果接口中的抽象方法中没有输入参数,不能使用对象方法引用 } public String toUpper(String str){ return str.toUpperCase(); } } class Too{ public void too(){ } public void top(String str){ } public String tos(String str){ return str; } }
三、stream API使用
Stream是一组用来处理数组、集合的API
Stream特性
1:不是数据结构,没有内部存储
2:不支持索引访问
3:延迟计算
4:支持并行
5:很容易生成数组或集合(List,Set)
6:支持过滤,查找,转换,汇总,聚合等操作
Stream运行机制
Stream分为 源source,中间操作,终止操作
流的源可以是一个数组、一个集合、一个生成器方法,一个I/O通道等等。
一个流可以有零个和或者多个中间操作,每一个中间操作都会返回一个新的流,供下一个操作使用。一个流只会有一个终止操作。
Stream只有遇到终止操作,它的源才开始执行遍历操作。
Stream常用API
中间操作:
过滤 filter
去重 distinct
排序 sorted
截取 limit、skip
转换 map/flatMap
其他 peek
终止操作:
循环 forEach
计算 min、max、count、 average
匹配 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny
汇聚 reduce
收集器 toArray collect
Stream的创建
1、通过数组
2、通过集合来
3、通过Stream.generate方法来创建
4、通过Stream.iterate方法来创建
5、其他API创建
实例演示:
package com.xiaomifeng1010.rbacboot.common.util; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; /** * @author xiaomifeng1010 * @version 1.0 * @date: 2020/3/20 0:03 */ public class StreamAPI { /** * 通过数组创建Stream */ static void generate1(){ String letter="a,b,c,d,e,f,g"; String[] array=letter.split(","); Stream<String> stream=Stream.of(array); stream.forEach(x -> System.out.println("generate1方法运行后:"+x)); } /** * 通过集合创建Steam */ static void generate2() { List<String> list= Arrays.asList("a","b","c","d"); Stream<String> stream=list.stream(); stream.forEach(x -> System.out.println("依次输出:"+x)); } /** * 通过Steam的静态方法generate()方法 */ static void generate3() { // generate()方法中参数类型是函数式接口Supplier,产生一个无止境的steam流 Stream<Integer> stream= Stream.generate(()->1); // 会死循环(会一直调用supplier接口的get方法) // stream.forEach(x -> System.out.println("generate3方法运行后:"+x)); // 因此为了避免死循环,可以在终止操作前使用中间操作limit截断流 stream.limit(10).forEach(x -> System.out.println("generate3方法运行后只取10个元素:"+x)); } /** * 通过Steam的静态方法iterate()方法 */ static void generate4(){ // iterate()方法,第一个参数是初始元素,第二个参数类型是函数式接口UnaryOperator(输入和输出类型相同) // UnaryOperator输入的x后,一直累加1,无限制累加1循环,生成无止境steam流,初始值为1,然后一直往后加1 Stream<Integer> stream= Stream.iterate(1,x -> x+1); // 会死循环(会一直调用iterate不停迭代) // stream.forEach(x -> System.out.println("generate4方法运行后:"+x)); // 因此为了避免死循环,可以在终止操作前使用中间操作limit截断流,取前十个数 stream.limit(10).forEach(x -> System.out.println("generate4方法运行后取前十位数:"+x)); } /** * 通过String类的chars()方法创建 */ static void generate5(){ String letters="abcd"; IntStream stream=letters.chars(); // 流的中间操作步骤可以有多步,或O部,为0则没有中间操作,可以直接使用终止操作,如直接操作stream的终止操作forEach: // stream.forEach(x -> System.out.println(x)); // 可以使用方法引用,会逐个输出a,b,c,d的ASCII码值(int类型) stream.forEach(System.out::println); } public static void main(String[] args) { // 第一种和第二种创建流的方式使用较多 // generate1(); // generate2(); // generate3(); // generate4(); // generate5(); // 演示stream中间操作,但是在终止操作前会延迟计算,并不会真正计算,需要终止操作才能进行计算 // Arrays.asList(1,2,3,4,5).stream().filter(x -> x%2==0); // 加上终止操作(过滤掉其他的数字,只保留偶数输出),filter()方法中参数是函数式接口Predicate<T>, // boolean test(T t); Arrays.asList(1,2,3,4,5).stream().filter(x -> x%2==0).forEach(System.out::println); // 过滤出来满足偶数的2,4,6的和,有返回值,可以终止 int sum= Arrays.asList(1,2,3,4,5,6).stream().filter(x -> x%2==0).mapToInt(x -> x).sum(); System.out.println("和为:"+sum); // 终止操作求最大值6 int max=Arrays.asList(1,2,3,4,5,6).stream().max((a,b) -> a-b).get(); System.out.println("最大值:"+max); // 查找满足过滤条件(偶数)中任一个 int anyone= Arrays.asList(1,2,3,4,5,6).stream().filter(x -> x%2==0).findAny().get(); System.out.println("查找满足过滤条件的任一个数据:"+anyone); // 查找满足过滤条件(偶数)的第一个(按排序从大到小 comparator中方法体用第二个参数减第一个参数) int findFirst= Arrays.asList(1,2,3,4,5,6).stream().filter(x -> x%2==0).sorted((a,b) -> b-a).findFirst().get(); System.out.println("查找满足过滤条件的第一个数据:"+findFirst); // 根据字符串长短排序(长度从小到大排序,并迭代输出) Arrays.asList("com","xiaomifeng1010","cn","admin").stream().sorted((a,b) -> a.length()-b.length()).forEach(System.out::println); // 从1-50里面的所有偶数查找出来,存放到一个list中 List<Integer> list=Stream.iterate(1,x -> x+1).limit(50).filter(x -> x%2==0).collect(Collectors.toList()); System.out.println(list); // 去重操作 Arrays.asList(1,3,4,5,6,3,5,7).stream().distinct().forEach(System.out::println); // 去重操作,将stream流转换成set集合类型(达到去重目的) Set<Integer> set= Arrays.asList(1,3,4,5,6,3,5,7).stream().collect(Collectors.toSet()); System.out.println("set集合元素:"+set); // 产生1-50数据,分页效果(从大到小排序后,跳过10个数据,放在list中) List<Integer> list2= Stream.iterate(1,x -> x+1 ).limit(50).sorted((a,b) -> b-a).skip(10).collect(Collectors.toList()); System.out.println("从50跳过10个数据后:"+list2); // 转换操作 String str="11,22,33,44,55"; // int sum2=Stream.of(str.split(",")).mapToInt(x -> Integer.parseInt(x)).sum(); // 写成方法引用 int sum2=Stream.of(str.split(",")).mapToInt(Integer::parseInt).sum(); System.out.println("字符串转换为int后求和:"+sum2); // 字符串对象,转换为Product对象 String productName="iphone,ipad,macbook,flashlight"; // Stream.of(productName.split(",")).map(x -> new Product(x)).forEach(System.out:: println); // 方法引用 Stream.of(productName.split(",")).map(Product::new).forEach(System.out:: println); // 演示peek操作,peek 操作接收的是一个 Consumer<T> 函数。顾名思义 peek 操作会按照 Consumer<T> // 函数提供的逻辑去消费流中的每一个元素,同时有可能改变元素内部的一些属性 Stream.of(productName.split(",")).peek(x -> x="hello"+x).forEach(System.out::println); // 当前的线程是主线程main,在peek方法中会输出main线程(串行方式,一直由main方法执行,即单线程) // 由于stream流支持并行,使用parallel(中间操作)开启并行(多个线程执行) // Optional<Integer> max2=Stream.iterate(1,x -> x+1).limit(200).peek(x -> System.out.println(Thread.currentThread().getName())) // .parallel().max(Integer::compare); // System.out.println(max); // 并行流转换成串行流 (使用sequential()方法) // Optional<Integer> max3=Stream.iterate(1,x -> x+1).limit(200).parallel().peek(x -> System.out.println(Thread.currentThread().getName())) // .sequential().max(Integer::compare); // System.out.println(max); // // } } class Product{ private String name; public Product(String name){ this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Product{" + "name='" + name + '\'' + '}'; } }
控制台输出结果:
单元测试:实战应用
package com.xiaomifeng1010.rbacboot.common.util; import lombok.AllArgsConstructor; import lombok.Data; import org.junit.Test; import java.time.LocalDate; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; /** * @author xiaomifeng1010 * @version 1.0 * @date: 2020/3/21 12:47 */ public class Java8LambdaTest { /** * 将请求的地址url中参数和值取出来存放到map中 * url:http:localhost:8080/index.do?itemId=1&userId=1000&type=2&token=134jojmgoijeo4&key=index */ @Test public void test1() { String queryString ="itemId=1&userId=1000&type=2&token=134jojmgoijeo4&key=index"; // 将字符串queryString按照&分割为数组,并创建stream流 Map<String,String> params= Stream.of(queryString.split("&")) // 转换为(返回包含输出结果类型元素的stream,进一步按照等号分割的数组,保存在stream中) .map(str -> str.split("=")) // collect方法收集,内部Collectors.toMap()转换为map类型 // toMap()方法中的参数为两个Function接口(第一个参数Function接口是获取map的key, // 第一个参数Function接口是获取map的value // Function接口中输入参数s是数组,输出是数组元素,key是数组第一个元素,value是数组第二个元素 // 因为是元素依次输入,所以其实每次的数组都是只有两个元素 .collect(Collectors.toMap(s -> s[0],s -> s[1])); System.out.println(params); } @Test public void test2 () { // 通过ComputerBooks()获取list,创建流,获取ComputerBook对象所有id,在存放到list中 List<Integer> ids = ComputerBooks().stream().map( ComputerBook -> ComputerBook.getId()).collect(Collectors.toList()); System.out.println(ids); // 方法引用ComputerBook::getId ids = ComputerBooks().stream().map(ComputerBook::getId).collect(Collectors.toList()); System.out.println(ids); // 获取ComputerBook对象所有id,然后以逗号方式连接拼接成字符串 String str = ComputerBooks().stream().map(ComputerBook -> ComputerBook.getId()+"").collect(Collectors.joining(",")); System.out.println(str); // 获取ComputerBook对象所有id,然后以逗号方式连接拼接成字符串,并且以括号包裹 str = ComputerBooks().stream().map(ComputerBook -> ComputerBook.getId()+"").collect(Collectors.joining(",", "(", ")")); System.out.println(str); str = ComputerBooks().stream().map(ComputerBook -> "'"+ComputerBook.getId()+"'").collect(Collectors.joining(",", "(", ")")); System.out.println(str); } @Test public void test3 () { // 通过ComputerBooks()获取list,创建流,获取ComputerBook对象所有类型,在存放到list中 // list中的泛型类型取决于map()方法转换输出类型(如本例中getType()方法) List<String> list = ComputerBooks().stream().map(ComputerBook::getType).collect(Collectors.toList()); System.out.println(list); // 通过ComputerBooks()获取list,创建流,获取ComputerBook对象所有类型(不重复的),在存放到list中 list = ComputerBooks().stream().map(ComputerBook::getType).distinct().collect(Collectors.toList()); System.out.println(list); // 上边的也可以通过存放在set达到去重 Set<String> set = ComputerBooks().stream().map(ComputerBook::getType).collect(Collectors.toSet()); System.out.println(set); } @Test public void test4 () { // 以ComputerBook类型的价格排序,价格由低到高排序 // ComputerBooks().stream().sorted((ComputerBook1, ComputerBook2) -> Double.compare(ComputerBook1.getPrice(), ComputerBook2.getPrice())).forEach(System.out::println);; // Comparator<ComputerBook> compa = (ComputerBook1, ComputerBook2) -> Double.compare(ComputerBook1.getPrice(), ComputerBook2.getPrice()); // 以ComputerBook类型的价格排序,价格由高到低排序(之前的排序规则反转一下) // ComputerBooks().stream().sorted(compa.reversed()).forEach(System.out::println); // 价格相同情况下,再比较出版日期 // Comparator<ComputerBook> compa = (ComputerBook1, ComputerBook2) -> Double.compare(ComputerBook1.getPrice(), ComputerBook2.getPrice()); // ComputerBooks().stream().sorted(compa.thenComparing((ComputerBook1,ComputerBook2) -> ComputerBook1.getPublishDate().isAfter(ComputerBook2.getPublishDate()) ? -1 : 1)).forEach(System.out::println); // ComputerBooks().stream().sorted(Comparator.comparing(ComputerBook::getPrice)).forEach(System.out::println); // ComputerBooks().stream().sorted(Comparator.comparing(ComputerBook::getPrice).reversed()).forEach(System.out::println); ComputerBooks().stream().sorted(Comparator.comparing(ComputerBook::getPrice).reversed().thenComparing(Comparator.comparing(ComputerBook::getPublishDate).reversed())).forEach(System.out::println); } @Test public void test5 () { // 将ComputerBook的id作为key,ComputerBook作为value存放到map中 // Map<Integer, ComputerBook> ComputerBooksMap = ComputerBooks().stream().collect(Collectors.toMap(ComputerBook -> ComputerBook.getId(), ComputerBook -> ComputerBook)); // System.out.println(ComputerBooksMap); Map<Integer, ComputerBook> ComputerBooksMap = ComputerBooks().stream().collect(Collectors.toMap(ComputerBook::getId, ComputerBook -> ComputerBook)); System.out.println(ComputerBooksMap); } @Test public void test6 () { // 统计平均价格(ComputerBook::getPrice对象方法引用) Double avg = ComputerBooks().stream().collect(Collectors.averagingDouble(ComputerBook::getPrice)); System.out.println(avg); } @Test public void test7 () { // 最高价的图书 Optional<ComputerBook> computerBook = ComputerBooks().stream().collect(Collectors.maxBy(Comparator.comparing(ComputerBook::getPrice))); System.out.println(computerBook); // 最低价的图书 computerBook = ComputerBooks().stream().collect(Collectors.minBy(Comparator.comparing(ComputerBook::getPrice))); System.out.println(computerBook); // 出版日期最早的书 computerBook = ComputerBooks().stream().collect(Collectors.minBy(Comparator.comparing(ComputerBook::getPublishDate))); System.out.println(computerBook); // 出版日期最晚的书 computerBook = ComputerBooks().stream().collect(Collectors.maxBy(Comparator.comparing(ComputerBook::getPublishDate))); System.out.println(computerBook); // 价格最高且日期最晚出版的图书(例子中有两本150的图书) Comparator<ComputerBook> comp = Comparator.comparing(ComputerBook::getPrice); computerBook = ComputerBooks().stream().collect(Collectors.maxBy(comp.thenComparing(Comparator.comparing(ComputerBook::getPublishDate)))); System.out.println(computerBook); } @Test public void test8 () { // 分组统计(按照类型),map中key为类型type // Map<String, List<ComputerBook>> ComputerBooksMap = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType)); // ComputerBooksMap.keySet().forEach(key -> { // System.out.println(key); // System.out.println(ComputerBooksMap.get(key)); // System.out.println("---------------------"); // }); // 按照类型分组统计,并计算每种类型的数量 // Map<String, Long> ComputerBooksCount = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType, Collectors.counting())); // System.out.println(ComputerBooksCount); // 按照类型分组统计,并计算每种类型图书的价格合计 // Map<String, Double> ComputerBooksSum = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType, Collectors.summingDouble(ComputerBook::getPrice))); // System.out.println(ComputerBooksSum); // 按照类型分组统计,并计算每种类型图书的平均价格 // Map<String, Double> ComputerBooksSum = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType, Collectors.averagingDouble(ComputerBook::getPrice))); // System.out.println(ComputerBooksSum); // 按照类型分组统计,并计算每种类型图书价格的最大值 // Map<String, Optional<ComputerBook>> ComputerBooksMaxPrice = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType, Collectors.maxBy(Comparator.comparing(ComputerBook::getPrice)))); // System.out.println(ComputerBooksMaxPrice); // 按照类型分组统计,并计算每种类型图书价格的最小值 // Map<String, Optional<ComputerBook>> ComputerBooksMinPrice = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType, Collectors.minBy(Comparator.comparing(ComputerBook::getPrice)))); // System.out.println(ComputerBooksMinPrice); // 每种类型出版时间最晚的 Map<String, Optional<ComputerBook>> ComputerBooksMaxPubDate = ComputerBooks().stream().collect(Collectors.groupingBy(ComputerBook::getType, Collectors.maxBy(Comparator.comparing(ComputerBook::getPublishDate)))); System.out.println(ComputerBooksMaxPubDate); } @Test public void test9 () { // 取出价格80元及以上的图书,按照出版日期从大到小排序 ComputerBooks().stream().filter(ComputerBook -> ComputerBook.getPrice() >= 80).sorted(Comparator.comparing(ComputerBook::getPublishDate).reversed()).forEach(System.out::println);; } private List<ComputerBook> ComputerBooks(){ List<ComputerBook> books = new ArrayList<>(); books.add(new ComputerBook(1, "tomcat", 70d, "服务器", LocalDate.parse("2014-05-17"))); books.add(new ComputerBook(2, "jetty", 60d, "服务器", LocalDate.parse("2015-12-01"))); books.add(new ComputerBook(3, "nginx", 65d, "服务器", LocalDate.parse("2016-10-17"))); books.add(new ComputerBook(4, "java", 66d, "编程语言", LocalDate.parse("2011-04-09"))); books.add(new ComputerBook(5, "ruby", 80d, "编程语言", LocalDate.parse("2013-05-09"))); books.add(new ComputerBook(6, "php", 40d, "编程语言", LocalDate.parse("2014-08-06"))); books.add(new ComputerBook(7, "html", 44d, "编程语言", LocalDate.parse("2011-01-06"))); books.add(new ComputerBook(8, "oracle", 150d, "数据库", LocalDate.parse("2013-08-09"))); books.add(new ComputerBook(9, "mysql", 66d, "数据库", LocalDate.parse("2015-04-06"))); books.add(new ComputerBook(10, "ssh", 70d, "编程语言", LocalDate.parse("2016-12-04"))); books.add(new ComputerBook(11, "设计模式", 81d, "其他", LocalDate.parse("2017-04-06"))); books.add(new ComputerBook(12, "重构", 62d, "其他", LocalDate.parse("2012-04-09"))); books.add(new ComputerBook(13, "敏捷开发", 72d, "其他", LocalDate.parse("2016-09-07"))); books.add(new ComputerBook(14, "从技术到管理", 42d, "其他", LocalDate.parse("2016-02-19"))); books.add(new ComputerBook(15, "算法导论", 66d, "其他", LocalDate.parse("2010-05-08"))); books.add(new ComputerBook(16, "oracle 12c", 150d, "数据库", LocalDate.parse("2017-05-08"))); return books; } } @Data @AllArgsConstructor class ComputerBook { private int id; private String name; private double price; private String type; private LocalDate publishDate; }
@Data和@AllArgsConstrucor注解需要引入lombok依赖,以及idea中安装lombok插件。
-
Java8 Lambda表达式和4大函数式接口
2019-12-29 15:50:17Lambda表达式操作的必须是函数式接口,也就是只有一个抽象方法的接口 package com.mock; import org.junit.jupiter.api.Test; import java.util.function.Consumer; import java.util.function.Function; import ... -
JDK1.8新特性:Lambda表达式语法和内置函数式接口
2019-01-04 21:59:56Lambda 是一个匿名函数,我们可以把 Lambda表达式理解为是一段可以传递的代码。JDK1.8提供了函数式接口支持Lambda语法开发 -
Java基础——Lambda表达式及四种内置函数式接口
2020-08-16 23:44:32Lambda表达式是JAVA8的新特性,使用Lambda表达式可以对一个函数式接口进行快速的实现(因此,lambda也可以看做是一个匿名函数。 使用注意: JAVA的版本必须为JAVA8及以上 并非所有接口都可以用Lambda表达式进行实现... -
四大函数式接口(lambda表达式、链式编程、函数式接口、stream流式计算)
2020-08-05 17:31:491.函数式接口:只用一个方法的接口 @FunctionalInterface public interface Runnable { public abstract void run(); } 2、lambda表达式:只要是函数型接口 可以 用 lambda表达式简化 // Function<String,... -
Java8新特性:Lambda表达式详解及四大函数式接口
2020-07-30 18:48:01Lambda 表达式是Java8以后的新特性,Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑,相应的带来了新的学习成本。 Lambda语法特征主要有以下四点... -
Lambda表达式入门--函数式编程与函数式接口
2020-12-31 22:50:14一,函数式编程: 函数式编程是基于函数式接口并使用lambda表达的编程方式 函数式编程理念是将代码...函数式接口:接口中有且只有一个抽象方法的接口 java中拥有大量函数式接口,比如java.lang.Runnable jdk8后提供 -
lambda表达式和函数式接口
2019-06-23 10:51:16文章目录1 Lambda 表达式1.1 lambda是一个匿名函数1.2 lambda应用1.3 lambda 语法1.4 Lambda 表达式需要“函数式接口”的支持2 函数式接口 1 Lambda 表达式 1.1 lambda是一个匿名函数 原先的匿名内部类 //例如 比较... -
Java学习笔记-Lambda表达式及內建函数式接口
2017-09-07 15:35:55lambda只能用于函数式接口。 lambda语法: ([形参列表,不带数据类型])-> { //执行语句 [return..;] } 注意: 1、如果形参列表是空的,只需要保留()即可 2、如果没有返回值。只需要在{}写执行语句即可 3、... -
lambda表达式函数式接口实现
2020-03-12 23:15:59函数式接口是java 8中的一个新特性,...首先我们通过 @FunctionalInterface 注解声明一个函数式接口,需要注意的是:函数式接口是指仅仅只包含一个抽象方法的特殊接口,每一个该类型的Lambda表达式都会被匹配到这个... -
lambda表达式与函数式接口
2020-05-02 23:33:09Java 8 新特性如下图所示,其中最重要的两个新特性是:Lambda表达式和Stream API。 Lambda表达式 Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段...Lambda表达式的本质:作为函数式接口的实例。 ... -
Lambda表达式2:函数式接口
2019-09-16 10:14:09可以在任意函数式接口使用@FunctionalInterface注解,可以检查其是否是一个函数式接口。 2、自定义函数接口 @FunctionalInterface public interface Test1<T> { public T test(T t); } 作为参... -
Lambda表达式和函数式接口
2021-02-14 20:01:04当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口。 Lambda是一个匿名函数,我们可以吧Lambda表达式理解为一段可以传递的代表(将代码像数据一样进行传递), 使用它可以写出更简洁,更灵活的代码。作为... -
java8学习之Lambda表达式继续探讨&函数式接口Function等详解
2019-12-08 14:43:20对于上次【http://www.cnblogs.com/webor2006/p/8186039.html】已经初步引入的Java...而这次专门对Function这个函数式接口进行进一步学习,因为这个函数式接口是比较重要的,先查看一下该接口的javadoc: 另外还有... -
Lambda表达式与函数式接口
2020-04-09 17:56:46lambda表达式是一个匿名函数,使的代码更加简单,简洁 函数式编程思想 面向对象过分强调必须通过对象的形式来做事,而函数式思想则尽量忽略面向 对象的复杂语法--强调做什么,而不是以什么形式做 jdk1.8加入... -
Lambda表达式与函数式接口的关系
2019-09-25 10:45:06Lambda表达式的类型,也被称为目标类型(targer type),Lambda表达式的目标类型必须是“函数式接口(functional interface)”。函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,... -
Java Lambda表达式与函数式接口
2020-04-22 17:35:14Lambda表达式与函数式接口基础学习 ...函数式接口是指只有一个抽象方法的接口; Lambda表达式需要函数式接口的支持; 自定义的函数式接口可以加上注解 @FunctionalInterface ,用于编译器检查是否是函数式接口。 ... -
lambda表达式及函数式接口
2019-07-20 14:43:36JDK8中新添加的lambda表达式及函数式接口: lambda表达式也可以称为匿名函数,它由一个参数和方法体组成 lambda表达式格式 (表达式) -> {执行语句} 使用原始的匿名类不类和lambda表达式进行比较,这里是... -
java8中Lambda表达式详解及函数式接口介绍
2019-03-11 17:23:52Lambda表达式 作为Java8 中最重要的新功能,使用 Lambda 表达式可以避免使用匿名内部类,代码看起来更简洁易懂。Lambda表达式还支持对集合、...只有一个抽象方法(Object类中的方法除外)的接口是函数式接口 主... -
Lambda表达式初步与函数式接口
2019-10-29 16:22:01而在javascript中,函数参数是一个函数,而返回值是另一个函数的情况是非常常见的,所以javascript是一门非常典型的函数式语言,也是一种面向多项的语言 gradle与maven相同都是约定优于配置(目录结构与... -
Lambda表达式在函数式接口中的使用
2020-09-18 17:44:51说到函数式接口就要说到一个注解 @FunctionalInterface,该注解是一个空的注解,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。接下来举几个例子: 由上述可知,... -
lambda表达式——函数式接口Comparator
2020-05-14 23:21:52lambda表达式——函数式接口Comparator一、函数式接口是什么?二、函数式接口的特点三、default关键字四、JDK中的函数式接口举例五、自定义Comparator排序 一、函数式接口是什么? 所谓的函数式接口,实际上就是接口... -
Lambda表达式 && 函数式接口
2020-03-06 10:54:56必须有上下问推断,即方法的参数或者局部变量必须是Lambda对应的接口类型。 标准格式 一些参数 一个箭头 一段代码 (参数列表)->{ 方法体 } 省略格式 参数列表的数据类型可以省略 若参数列表中只有一个参数时...
-
2020下半年数据库系统工程师下午真题及答案解析.pdf
-
PHP-从入门到精通-vidio 零基础学习必看视频教程分享30G资料19天完整视频课
-
C/C++反汇编解密
-
i++是原子性的吗
-
小型在线聊天室php版
-
【爱码农】C#制作MDI文本编辑器
-
rereschrome.zip
-
《文件和目录操作命令》
<2.> -
1.5: RPM软件包管理 、 Yum软件包仓库 、 命令补充.docx
-
基于微信的同城小程序、校园二手交易小程序 毕业设计毕设源码使用教程
-
MySQL 存储过程(创建海量数据实验环境)
-
OpenGL入门教程
-
pre
-
C/C++:哈夫曼编译码器.rar(含完整注释)
-
《python数据分析与数据化运营》5.rar
-
[自测]linux运维100题目(阿里巴巴内部题目)
-
安卓入门开发教程!2021年Android面试心得,讲的明明白白!
-
MySQL 高可用(DRBD + heartbeat)
-
安卓原生开发!终于有人把安卓程序员必学知识点全整理出来了,最强技术实现
-
2.1: 基本权限和归属 、 附加权限 、 ACL策略管理.docx