相关链接
目录
- 1.Lambda表达式 - 简介
- 2.Lambda表达式 - 基础语法
- 3.Lambda表达式 - 方法引用 ::
- 4.Lambda表达式 - 构造方法引用
- 5.Lambda表达式 - 案例
- 5.1 案例1 - List集合排序(List.sort - Comparator)
- 5.2 案例2 - TreeSet集合排序
- 案例2.1 - TreeSet集合排序(TreeSet - 原理)
- 案例2.2 - TreeSet集合排序(Person - 年龄排序)
- 案例2.3 - TreeSet集合排序(Person - 姓名排序)
- 案例2.4 - TreeSet集合排序(TreeSet - 年龄排序)
- 案例2.5 - TreeSet集合排序(TreeSet - 姓名排序)
- 🌟 TreeSet集合排序(Comparable/Comparator - 原理)
- 案例2.6 - TreeSet - freeStyle(姓名排序、不去重)
- 案例2.7 - TreeSet - freeStyle(不排序、姓名去重)
- 案例2.8 - TreeSet - freeStyle(多条件排序)
- 案例2.9 - TreeSet - freeStyle(测试 - 同时排序)
- 5.3 案例3 - ArrayList.forEach遍历
- 5.4 案例4 - ArrayList.removeIf条件删除
- 5.5 案例5 - 多线程
- 5.6 案例6 - Stream流
- 6. 系统内置函数式接口
- 7. 闭包
- 8. lambda使用技巧
1.Lambda表达式 - 简介
1.什么是Lambda?
Lambda是JAVA 8添加的一个新的特性,是一种基于匿名函数的更简洁的写法。也叫做闭包,允许把函数作为参数传递进方法中。这种思想来自于数学逻辑中的 λ演算。
抽象类abstract | 接口interface(含有多个抽象方法) | 接口interface(含有单个抽象方法) | |
---|---|---|---|
1.实现类 | ✓ | ✓ | ✓ |
2.匿名内部类 | ✓ | ✓ | ✓ |
3.Lambda表达式 | X | X | ✓ |
—— 三种实现方式的适用范围对比
2.为什么要使用Lambda?
可以更简洁调用某些接口
3.Lambda对接口的要求?
接口中定义的必须要实现的抽象方法只有一个
JDK1.8接口新特性(使用Lambda表达式时会经常见到这两个新特性)
1️⃣ 修饰符 default
原本接口中的方法只能是抽象的不能实现,default 修饰的方法,可以在接口中有一个默认实现
2️⃣ 注解 @FunctionalInterface
修饰函数式接口的。@FunctionalInterface修饰的接口中,添加了限制,抽象方法只能有一个。(也就是说 @FunctionalInterface 修饰的接口可以使用Lambda表达式)
@FunctionalInterface
interface Calculator {
/**
* 减法
* @param subtraction 减数
* @param minuend 被减数
* @return 差
*/
int reduce(int subtraction, int minuend);
/**
* 加法
* @param a 加数a
* @param b 加数b
* @return 和
*/
default int add(int a, int b) {
return a + b;
}
}
2.Lambda表达式 - 基础语法
2.1 Lambda入门(普通->匿名->lambda)
语法简化路线:
1.普通函数:返回值类型 方法名 参数列表 方法体
/* 方式一:【普通类】实现
A:定义一个MyCalculator类,继承Calculator
B:重写reduce()方法
C:多态方式创建Calculator类的对象,调用reduce()方法 */
class MyCalculator implements Calculator {
@Override
public int reduce(int subtraction, int minuend) {
return subtraction - minuend;
}
}
Calculator myCalculator = new MyCalculator();
int result1 = myCalculator.reduce(2, 1);
2.匿名函数:返回值类型 参数列表 方法体
/* 方式二:【匿名内部类】实现 */
int result2 = new Calculator() {
@Override
public int reduce(int subtraction, int minuend) {
return subtraction - minuend;
}
}.reduce(3, 1);
3.Lambda表达式:参数列表 -> 方法体
//最终还可以简写为以下方式,详细语法查看 => 2.3 Lambda省略语法
//myCalculator = (a, b) -> a - b;
myCalculator = (int a, int b) -> {
return a - b;
};
int result3 = myCalculator.reduce(4, 1);
4.Lambda表达式:省略语法
myCalculator = (int a, int b) -> a - b;
int result4 = myCalculator.reduce(5, 1);
案例代码一 Lambda入门(3种方式对比)
介绍: 三种方式实现接口(单抽象方法)
1. 接口的实现类;
2. 使用匿名内部类;(不需要创建实现类,代码较长)
3. Lambda表达式;(不需要创建实现类,代码较短,但新手看不懂)
接口列表:
1. Calculator -> 只有一个未实现方法的接口,可以被Lambda表达式实现
类列表:
2. MyCalculator -> Calculator接口的实现类
3. Demo1Lambda -> 测试类
package com.groupies.jdk8.day01;
/**
* @author GroupiesM
* @date 2021/09/23
* @introduction Lambda入门(3种方式对比)
*/
@FunctionalInterface//修饰函数式接口的。@FunctionalInterface修饰的接口中,抽象方法只有一个。
interface Calculator {
/**
* 减法
* @param subtraction 减数
* @param minuend 被减数
* @return 差
*/
int reduce(int subtraction, int minuend);
/**
* 加法
* @param a 加数a
* @param b 加数b
* @return 和
*/
default int add(int a, int b) {
return a + b;
}
}
/**
* 方式一:接口的实现类
*/
class MyCalculator implements Calculator {
/**
* @introduction 传入两个数字类型参数,实现a-b
* @param subtraction 被减数
* @param minuend 减数
*/
@Override
public int reduce(int subtraction, int minuend) {
return subtraction - minuend;
}
}
public class Demo1Lambda {
static Calculator myCalculator;
public static void main(String[] args) {
//方式一:创建一个接口实现类
myCalculator = new MyCalculator();
int result1 = myCalculator.reduce(2, 1);
//方式二:使用匿名内部类(不需要创建实现类)
int result2 = new Calculator() {
@Override
public int reduce(int subtraction, int minuend) {
return subtraction - minuend;
}
}.reduce(3, 1);
//☆方式三:使用Lambda表达式(不需要创建实现类)
//最终还可以简写为 myCalculator = (a, b) -> a - b;,详细语法查看 => 2.3 Lambda省略语法
myCalculator = (int a, int b) -> {
return a - b;
};
int result3 = myCalculator.reduce(4, 1);
System.out.println("result1: " + result1);//result1: 1
System.out.println("result2: " + result2);//result2: 2
System.out.println("result3: " + result3);//result3: 3
}
}
2.2 Lambda基础语法(6种接口)
本节需要创建两个类,接口类和测试类,在测试类中通过Lambda表达式实现接口类
接口列表:
1. LambdaNoneReturnNoneParameter -> 无返回值无参数接口
2. LambdaNoneReturnSingleParameter -> 无返回值单个参数接口
3. LambdaNoneReturnMultipleParameter -> 无返回值多个参数接口
4. LambdaSingleReturnNoneParameter -> 单个返回值无参数接口
5. LambdaSingleReturnSingleParameter -> 单个返回值单个参数接口
6. LambdaSingleReturnMultipleParameter -> 单个返回值多个参数接口
类列表:
1. Demo2LambdaSyntax -> Lambda表达式基础语法(6种接口)的测试类
案例代码二 Lambda基础语法(6种接口) 接口类
package com.groupies.jdk8.day01;
/**
* @author GroupiesM
* @date 2021/09/26
* @introduction
*/
public interface DemoInterface {
}
/**
* @introduction 无返回值无参数接口
*/
@FunctionalInterface
interface LambdaNoneReturnNoneParameter {
void test();
}
/**
* @introduction 无返回值单个参数接口
*/
@FunctionalInterface
interface LambdaNoneReturnSingleParameter {
void test(int a);
}
/**
* @introduction 无返回值多个参数接口
*/
@FunctionalInterface
interface LambdaNoneReturnMultipleParameter {
void test(int a, int b);
}
/**
* @introduction 单个返回值无参数接口
*/
@FunctionalInterface
interface LambdaSingleReturnNoneParameter {
int test();
}
/**
* @introduction 单个返回值单个参数接口
*/
@FunctionalInterface
interface LambdaSingleReturnSingleParameter {
int test(int a);
}
/**
* @introduction 单个返回值多个参数接口
*/
@FunctionalInterface
interface LambdaSingleReturnMultipleParameter {
int test(int a, int b);
}
案例代码二 Lambda基础语法(6种接口) 测试类
package com.groupies.jdk8.day01;
/**
* @author GroupiesM
* @date 2021/09/26
* @introduction Lambda语法:Lambda基础语法(6种接口)
*/
public class Demo2LambdaSyntax {
public static void main(String[] args) {
// 1.Lambda表达式的基础语法: 参数列表 方法体
// () : 描述参数列表
// -> : Lambda运算符,读作goes to
// {} : 描述方法体
// 1.无返回值无参数接口 LambdaNoneReturnNoneParameter
LambdaNoneReturnNoneParameter lambda1 = () -> {
System.out.println("1.无返回值无参数接口:" + 1);
};
lambda1.test();
// 2.无返回值单个参数接口 LambdaNoneReturnSingleParameter
LambdaNoneReturnSingleParameter lambda2 = (int a) -> {
System.out.println("2.无返回值单个参数(3)接口:" + a * 2);
};
lambda2.test(3);
// 3.无返回值多个参数接口 LambdaNoneReturnNoneParameter
LambdaNoneReturnMultipleParameter lambda3 = (int a, int b) -> {
System.out.println("3.无返回值多个参数(3,4)接口:" + a * b);
};
lambda3.test(3, 4);
// 4.单个返回值无参数接口 LambdaSingleReturnNoneParameter
LambdaSingleReturnNoneParameter lambda4 = () -> {
return 4;
};
int test4 = lambda4.test();
System.out.println("4.单个返回值无参数接口:" + test4);
// 5.单个返回值单个参数接口 LambdaSingleReturnSingleParameter
LambdaSingleReturnSingleParameter lambda5 = (int a) -> {
return a * 4;
};
int test5 = lambda5.test(3);
System.out.println("5.单个返回值单个参数(3)接口:" + test5);
// 6.单个返回值多个参数接口 LambdaSingleReturnMultipleParameter
LambdaSingleReturnMultipleParameter lambda6 = (int a, int b) -> {
return a * b * 2;
};
int test6 = lambda6.test(3, 4);
System.out.println("6.单个返回值多个参数(3,4)接口:" + test6);
}
}
2.3 Lambda省略语法(4种场景)🌟
序号 | 省略语法 |
---|---|
1 | 参数类型: 由于在接口的抽象方法中,已经定义了参数的数量和类型。所以在lambda表达式中,参数的类型可以省略 备注: 如果要省略类型,则每一个参数的类型都要省略,不能有的省略参数类型,有的不省略参数类型 |
2 | 参数小括号(): 如果参数列表中,参数的数量只有1个。此时小括号可以省略(小括号省略时,参数类型也必须省略) |
3 | 方法大括号{}: 如果方法体中只有一条语句,此时大括号可以省略 |
4 | return: 如果方法体中唯一的一条语句是一个return语句,省略掉大括号的同时,也必须省略掉return |
案例代码三 Lambda省略语法(4种方式)
接口列表:
1. LambdaNoneReturnSingleParameter -> 无返回值单个参数接口(引用代码二中创建的通用接口)
类列表:
1. Demo3LambdaSyntax -> 测试类
package com.groupies.jdk8.day01;
/**
* @author GroupiesM
* @date 2021/09/26
* @introduction 语法:Lambda省略语法(4种场景)
*/
public class Demo3LambdaSyntax {
public static void main(String[] args) {
//1.参数:由于在接口的抽象方法中,已经定义了参数的数量和类型。所以在lambda表达式中,参数的类型可以省略
LambdaNoneReturnMultipleParameter lambda1;
lambda1 = (int a, int b) -> {System.out.println(a * b);};
//备注:如果要省略类型,则每一个参数的类型都要省略,不能有的省略参数类型,有的不省略参数类型
lambda1 = (a, b) -> {System.out.println(a * b);};//☆
lambda1.test(3, 4);
//2.参数小括号:如果参数列表中,参数的数量只有一个。此时小括号可以省略(小括号省略时,参数类型也必须省略)
LambdaSingleReturnSingleParameter lambda2;
lambda2 = (int a) -> {return a * 4;};
lambda2 = a -> {return a * 4;};//☆
lambda2.test(3);
//3.方法大括号:如果方法体中只有一条语句,此时大括号可以省略
LambdaNoneReturnSingleParameter lambda3;
lambda3 = (int a) -> {System.out.println(a * 2);};
lambda3 = (int a) -> System.out.println(a * 2);//☆
lambda3 = (a) -> System.out.println(a * 2);
lambda3 = a -> System.out.println(a * 2);
lambda3.test(3);
//4. 如果方法体中唯一的一条语句是一个return语句,省略掉大括号的同时,也必须省略掉return
LambdaSingleReturnSingleParameter lambda4;
lambda4 = (int a) -> {return a * 4;};
lambda4 = (int a) -> a * 4;//☆
lambda4 = (a) -> a * 4;
lambda4 = a -> a * 4;
lambda4.test(3);
}
}
3.Lambda表达式 - 方法引用 ::
a.方法引用:
1. 通过Lambda表达式+方法引用,使代码更精简
2. 接口的实现是通过 回调函数(callback,简单理解为一个内存地址),指向另一个同参、同返回值的实现方法;
同参: 参数数量和类型,一定要和接口中定义的方法一致;
同返回值: 返回值的类型一定要和接口中定义的一致;
b.语法:
序号 | 类型 | 语法 | Lambda表达式 |
---|---|---|---|
1 | 静态方法引用 | 类名::静态方法名称 | (args)->new 类名 |
2 | 对象方法引用 | 类名::实例方法名称 | |
3 | 实例方法引用 | 对象名::方法引用 | |
4 | 构造方法引用 | 类名::new |
1. 静态方法引用 类名::静态方法名称
2. 对象方法引用 类名::实例方法名称
3. 实例方法引用 对象名::方法引用(先new对象 )
4. 构造方法引用 类名::new
3.1 静态方法引用
案例代码四 Lambda表达式的方法引用 - 1. 静态方法引用
public class Demo4LambdaMethodReference {
public static void main(String[] args) {
//1. 静态方法引用
//方式1:类名::静态方法名称
IConvert<Integer, Integer> doubleInt1 = Demo4IntegerUtils::doubleInt;
System.out.println(doubleInt1.convert(10));//输出 20
//方式2:传统lambda表达式
IConvert<Integer, Integer> doubleInt2 = num -> Demo4IntegerUtils.doubleInt(num);
System.out.println(doubleInt2.convert(5));//输出 10
}
//静态方法:数字*2并返回
public static class Demo4IntegerUtils { private static int doubleInt(int a) {return a * 2;}}
/**
* 通用构造器
* @param <T>
* @param <R>
*/
@FunctionalInterface
interface IConvert<T, R> { T convert(R arg);}
}
3.2 对象方法引用
案例代码五 Lambda表达式的方法引用 - 2. 对象方法引用
public class Demo5LambdaMethodReference {
public static void main(String[] args) {
//2. 对象方法引用
//方式1: 类名::实例方法名称
IConvert<Integer, IntegerUtils, Integer> tripleInt = IntegerUtils::tripleInt;
Integer convert = tripleInt.convert(new IntegerUtils(), 10);
System.out.println(convert);
//方式2:传统lambda表达式(非静态方法,不可直接使用)
//IntegerUtils.tripleInt;
}
//非静态方法:数字*3并返回
public static class IntegerUtils { private int tripleInt(int a) {
return a * 3;
}}
/**
* 通用构造器
* @param <T> 返回值
* @param <R> 入参
* @param <E> 入参
*/
@FunctionalInterface
interface IConvert<T, R, E> { T convert(R agr1, E arg2);}
}
3.3 实例方法引用
案例代码六 Lambda表达式的方法引用 - 3. 实例方法引用
public class Demo6LambdaMethodReference {
public static void main(String[] args) {
//3. 实例方法引用
//方式1: 对象名::方法引用(先new对象 )
Util util = new Util();
IConvert<Integer, Integer> convert1 = num -> util.quadrupleInt(num);
System.out.println(convert1.get(2));//8
//方式2:传统lambda表达式
IConvert<Integer, Integer> convert2 = util::quadrupleInt;
System.out.println(convert2.get(3));//12
}
//非静态方法:数字*4并返回
public static class Util {public Integer quadrupleInt(int num) {return num * 4;}}
/**
* 通用构造器
* @param <T> 返回值
* @param <R> 参数
*/
@FunctionalInterface
interface IConvert<T, R> { T get(R arg);}
}
3.4 构造方法引用
案例代码七 Lambda表达式的方法引用 - 4. 构造函数引用
public class Demo7LambdaMethodReference {
public static void main(String[] args) {
//4. 构造函数引用
//方式1: 类名::new
IConvert<Person, String, Integer> constructor1 = Person::new;
constructor1.create("李四", 6);
//方式2: 传统lambda表达式
IConvert<Person, String, Integer> constructor2 = (name, age) -> new Person(name, age);
constructor2.create("张三", 5);
}
/**
* 实体类
*/
public static class Person {
String name;
Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Integer getAge() {return age;}
public void setAge(Integer age) {this.age = age;}
}
/**
* 通用构造器
* @param <T> 返回值
* @param <R> 参数1
* @param <E> 参数2
*/
@FunctionalInterface
interface IConvert<T, R, E> { T create(R agr1, E arg2);}
}
4.Lambda表达式 - 构造方法引用
案例代码八 Lambda表达式 - 构造方法引用
接口列表:
1. PersonCreatorNoneParameter -> 无返回值单个参数接口(Person类构造器接口)
2. PersonCreatorDoubleParameter -> 无返回值单个参数接口(Person类构造器接口)
3. GenericCreatorNoneParameter -> 无返回值单个参数接口(通用构造器接口)
4. GenericCreatorDoubleParameter -> 无返回值单个参数接口(通用构造器接口)
类列表:
1. Demo8Person -> 实体类,有两个属性(name,age);在测试类中引用这个类的构造方法
2. Demo8LambdaSyntax -> 测试类
package com.groupies.jdk8.day01;
/**
* @author GroupiesM
* @date 2021/09/26
* @introduction 构造方法的引用
*/
//Person实体类
class Demo8Person {
public String name;
public Integer age;
public Demo8Person() {System.out.println("Person类的无参构造方法执行了");}
public Demo8Person(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("Person类的有参构造方法执行了");
}
public Demo8Person(Object name, Object age) {
this.name = name.toString();
this.age = Integer.valueOf(age.toString());
System.out.println("Person类的有参(Object)构造方法");
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
@Override
public String toString() {return "Person{name='" + name + "', age='" + age + "'}";}
}
//Person类-无参构造器
@FunctionalInterface
interface PersonCreatorNoneParameter {
Demo8Person getPerson();
}
//Person类-2参构造器
@FunctionalInterface
interface PersonCreatorDoubleParameter { Demo8Person getPerson(String name, int age);}
//通用-无参构造器
@FunctionalInterface
interface GenericCreatorNoneParameter<T> { T getObject();}
//通用-2参构造器
@FunctionalInterface
interface GenericCreatorDoubleParameter<T, R, E> { T getObject(R o1, E o2);}
//测试类
public class Demo8LambdaSyntax {
//Person类-无参构造器
static PersonCreatorNoneParameter personNoneParam;
//Person类-2参构造器
static PersonCreatorDoubleParameter personDoubleParam;
//通用-无参构造器
static GenericCreatorNoneParameter genericNoneParam;
//通用-2参构造器
static GenericCreatorDoubleParameter genericDoubleParam;
public static void main(String[] args) {
//方式1.lambda表达式调用接口
System.out.println("=====1.lambda表达式调用接口");
personNoneParam = () -> new Demo8Person();
Demo8Person a = personNoneParam.getPerson();
System.out.println(a.toString());//Person{name='null', age=null}
//方式2.无参构造方法的引用(new代表构造方法)
System.out.println("=====2.无参构造方法的引用(new代表构造方法)");
//2.1 Person类-无参构造器
personNoneParam = Demo8Person::new;
Demo8Person b1 = personNoneParam.getPerson();
System.out.println(b1.toString());//Person{name='null', age=null}
//2.2 通用-无参构造器
genericNoneParam = Demo8Person::new;
Demo8Person b2 = (Demo8Person) genericNoneParam.getObject();
System.out.println(b2.toString());//Person{name='null', age=null}
//3.带参构造方法的引用
System.out.println("=====3.带参构造方法的引用");
//3.1 Person类-2参构造器
personDoubleParam = Demo8Person::new;
Demo8Person c1 = personDoubleParam.getPerson("lisi", 15);//Person{name='lisi', age=15}
System.out.println(c1.toString());
//3.2 通用-2参构造器
genericDoubleParam = Demo8Person::new;
Demo8Person c2 = (Demo8Person) genericDoubleParam.getObject("zhangsan", 10);//Person{name='zhangsan', age=10}
System.out.println(c2.toString());
}
}
5.Lambda表达式 - 案例
5.1 案例1 - List集合排序(List.sort - Comparator)
实现排序的核心方法:
1. ArrayList对象的sort方法,需要传入一个Comparator接口对象作为参数;
//ArrayList对象的sort方法,需要传入一个Comparator接口对象
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
//Comparator接口由@FunctionalInterface修饰
//Comparator接口的抽象方法只有compare
//equals方法继承自Object,不需要实现
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
...
}
2. 而引入Comparator参数,需要先实现Comparator接口中未实现的方法compare。
通过Lambda语法实现Comparator接口的compare方法,并将其作为参数传入ArrayList类的sort方法中。
ArrayList<Demo9Person> list = new ArrayList<>();
//Lambda表达式:年龄降序
list.sort((o1, o2) -> o2.age - o1.age);
System.out.println("Lambda->按照年龄降序:" + list);
3.通过Lambda语法实现Comparator接口的comparing方法。comparing方法将返回一个已经实现了compare方法的Comparator对象,并将其作为参数传入ArrayList类的sort方法中。
@FunctionalInterface
public interface Comparator<T> {
...
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
...
}
ArrayList<Demo6Person> list = new ArrayList<>();
//Lambda表达式:年龄升序
list.sort(Comparator.comparing(Demo9Person::getAge));
System.out.println("Lambda->按照年龄升序:" + list);
//Lambda表达式:姓名升序
list.sort(Comparator.comparing(Demo9Person::getName));
System.out.println("Lambda->按照姓名升序:" + list);
案例代码九 Lambda表达式案例 - List集合排序(Comparator)
类列表:
1. Demo9Person -> 实体类,有两个属性(name,age);在测试类中制定规则,对这个实体类的List对象进行排序
2. Demo9LambdaListSort -> 测试类
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.Comparator;
/**
* @author GroupiesM
* @date 2021/09/26
* @introduction 案例1 - List集合排序(List.sort - Comparator)
* 需求:Person类集合按照年龄降序排序
*/
class Demo9Person {
public String name;
public int age;
public Demo9Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "(n=" + name + ",a=" + age + ")";
}
}
public class Demo9LambdaListSort {
public static void main(String[] args) {
ArrayList<Demo9Person> list = new ArrayList<>();
list.add(new Demo9Person("人类7", 2));
list.add(new Demo9Person("人类6", 1));
list.add(new Demo9Person("人类5", 3));
list.add(new Demo9Person("人类4", 4));
list.add(new Demo9Person("人类3", 5));
list.add(new Demo9Person("人类1", 6));
list.add(new Demo9Person("人类2", 7));
System.out.println("\t\t\t 排序前:" + list);
//Lambda表达式:年龄降序
list.sort((o1, o2) -> {
return o2.age - o1.age;
});//可以简写为下面一行
list.sort((o1, o2) -> o2.age - o1.age);
System.out.println("Lambda->按照年龄降序:" + list);
//Lambda表达式:年龄升序
list.sort(Comparator.comparing(Demo9Person::getAge));
System.out.println("Lambda->按照年龄升序:" + list);
//Lambda表达式:姓名升序
list.sort(Comparator.comparing(Demo9Person::getName));
System.out.println("Lambda->按照姓名升序:" + list);
//自定义排序
list.sort(new Comparator<Demo9Person>() {
@Override
public int compare(Demo9Person o1, Demo9Person o2) {
return o1.age - o2.age;
}
});
System.out.println("自定义\t按照年龄升序:" + list);
}
}
案例九 - 执行结果
排序前:[(n=人类7,a=2), (n=人类6,a=1), (n=人类5,a=3), (n=人类4,a=4), (n=人类3,a=5), (n=人类1,a=6), (n=人类2,a=7)]
Lambda->按照年龄降序:[(n=人类2,a=7), (n=人类1,a=6), (n=人类3,a=5), (n=人类4,a=4), (n=人类5,a=3), (n=人类7,a=2), (n=人类6,a=1)]
Lambda->按照年龄升序:[(n=人类6,a=1), (n=人类7,a=2), (n=人类5,a=3), (n=人类4,a=4), (n=人类3,a=5), (n=人类1,a=6), (n=人类2,a=7)]
Lambda->按照姓名升序:[(n=人类1,a=6), (n=人类2,a=7), (n=人类3,a=5), (n=人类4,a=4), (n=人类5,a=3), (n=人类6,a=1), (n=人类7,a=2)]
自定义 按照年龄升序:[(n=人类6,a=1), (n=人类7,a=2), (n=人类5,a=3), (n=人类4,a=4), (n=人类3,a=5), (n=人类1,a=6), (n=人类2,a=7)]
5.2 案例2 - TreeSet集合排序
案例2.1 - TreeSet集合排序(TreeSet - 原理)
a.TreeSet原理:
1. TreeSet集合有两个特性(排序和去重),但需要先指定排序规则(通过实现Comparable接口);
2. TreeSet集合中的元素有多个属性时,可以手动指定排序规则
3. 指定排序方式有两种,一是TreeSet中对象实现Comparable接口,重写compareTo方法(参考案例十中TreeSet<Integer>);二是使用TreeSet的带参构造
public final class Integer extends Number implements Comparable<Integer> {...}
4. 排序对比:
4.1 ArrayList的手动调用sort方法时排序一次;
4.2 TreeSet的每个元素进入时都会自动排序,且会根据排序规则去重
5. 接口对比:
5.1 ArrayList的sort方法需要实现Comparator接口
5.2 TreeSet对象排序的方式一需要实现Comparable接口
5.3 TreeSet对象排序的方式二需要实现Comparator接口
6. 不指定排序时,会出现类型转换错误ClassCastException(案例十)
7. TreeMap和TreeSet性质类似,可以自己类比做练习
案例代码十 TreeSet集合排序(TreeSet介绍)
b.类列表:
1. Demo10Person -> 实体类,有两个属性(name,age)
2. Demo10LambdaTreeSet -> 测试类
package com.groupies.jdk8.day01;
import java.util.TreeSet;
/**
* @author GroupiesM
* @date 2021/10/11
* @introduction 案例2.1 - TreeSet集合排序(TreeSet原理介绍)
*/
class Demo10Person {
public String name;
public int age;
public Demo10Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "(n=" + name + ",a=" + age + ")";
}
}
public class Demo10LambdaTreeSet {
public static void main(String[] args) {
//TreeSet集合会自动对其中的元素进行排序
//所以存放的对象必须实现Comparable接口
//Integer类实现了Comparable接口;测试时,乱序放入三个数字,TreeSet中元素按从小到大排序
TreeSet<Integer> set1 = new TreeSet<>();
set1.add(5);
set1.add(6);
set1.add(2);
set1.add(2);
//[2, 5, 6]
System.out.println(set1);
//而Demo10Person类没有实现Comparable接口
//TreeSet集合对元素进行排序时,出现类型转换错误ClassCastException
TreeSet<Demo10Person> set2 = new TreeSet<>();
set2.add(new Demo10Person("人类3", 5));
set2.add(new Demo10Person("人类1", 3));
set2.add(new Demo10Person("人类1", 2));//姓名重复、年龄不重复
set2.add(new Demo10Person("人类2", 2));//年龄重复、姓名不重复
//报错信息:
//Exception in thread "main" java.lang.ClassCastException
//com.groupies.jdk8.day01.Demo10Person cannot be cast to java.lang.Comparable
System.out.println(set2);
}
}
案例2.2 - TreeSet集合排序(Person - 年龄排序)
a.TreeSet中的对象实现排序:
1. Person类实现Comparable接口,指定排序规则(年龄age)
class Demo11Person implements Comparable<Demo11Person> {
...
//按照年龄降序
@Override
public int compareTo(Demo11Person p) {
return p.age - this.age; //年龄降序 -> [(n=人类3,a=5), (n=人类1,a=3), (n=人类1,a=2)]
//return p.age.compareTo(this.age);//年龄降序 -> [(n=人类3,a=5), (n=人类1,a=3), (n=人类1,a=2)]
//return this.age - p.age; //年龄升序 -> [(n=人类1,a=2), (n=人类1,a=3), (n=人类3,a=5)]
//return this.age.compareTo(p.age);//年龄升序 -> [(n=人类1,a=2), (n=人类1,a=3), (n=人类3,a=5)]
}
}
2. 按照年龄排序时,也就表示TreeSet<Person>中年龄相等的元素会去重。
案例代码十一 TreeSet集合排序(Person - 年龄排序)
b.类列表:
1. Demo11Person -> 实体类,有两个属性(name,age),实现了Comparable排序接口
2. Demo11LambdaTreeSet -> 测试类
package com.groupies.jdk8.day01;
import java.util.TreeSet;
/**
* @author GroupiesM
* @date 2021/11/15
* @introduction 案例2.2 - TreeSet集合排序(Person - 年龄排序)
*/
class Demo11Person implements Comparable<Demo11Person> {
public String name;
public Integer age;
public Demo11Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "(n=" + name + ",a=" + age + ")";
}
//年龄排序
@Override
public int compareTo(Demo11Person p) {
return p.age - this.age; //年龄降序 -> [(n=人类3,a=5), (n=人类1,a=3), (n=人类1,a=2)]
//return p.age.compareTo(this.age);//年龄降序 -> [(n=人类3,a=5), (n=人类1,a=3), (n=人类1,a=2)]
//return this.age - p.age; //年龄升序 -> [(n=人类1,a=2), (n=人类1,a=3), (n=人类3,a=5)]
//return this.age.compareTo(p.age);//年龄升序 -> [(n=人类1,a=2), (n=人类1,a=3), (n=人类3,a=5)]
}
}
public class Demo11LambdaTreeSet {
public static void main(String[] args) {
TreeSet<Demo11Person> set = new TreeSet<>();
set.add(new Demo11Person("人类3", 5));
set.add(new Demo11Person("人类1", 3));
set.add(new Demo11Person("人类1", 2));//姓名重复、年龄不重复
set.add(new Demo11Person("人类2", 2));//年龄重复、姓名不重复
//Lambda表达式:年龄排序
System.out.println(set);
}
}
案例2.3 - TreeSet集合排序(Person - 姓名排序)
a.TreeSet中的对象实现排序:
1. Person类实现Comparable接口,指定排序规则(姓名name)
class Demo12Person implements Comparable {
...
//姓名排序
@Override
public int compareTo(Demo12Person p) {
return p.name.compareTo(
this.name);//姓名降序(字典序) -> [(n=人类3,a=5), (n=人类2,a=2), (n=人类1,a=3)]
//return this.name.compareTo(
//p.name); //姓名升序(字典序) -> [(n=人类1,a=3), (n=人类2,a=2), (n=人类3,a=5)]
}
}
2. 按照姓名排序时,也就表示TreeSet<Person>中姓名相等的元素会去重。
案例代码十二 TreeSet集合排序(Person - 姓名排序)
b.类列表:
1. Demo12Person -> 实体类,有两个属性(name,age),实现了Comparable排序接口
2. Demo12LambdaTreeSet -> 测试类
package com.groupies.jdk8.day01;
import java.util.TreeSet;
/**
* @author GroupiesM
* @date 2021/11/15
* @introduction 案例2.3 - TreeSet集合排序(Person - 姓名排序)
*/
class Demo12Person implements Comparable<Demo12Person> {
public String name;
public Integer age;
public Demo12Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "(n=" + name + ",a=" + age + ")";
}
//姓名排序
@Override
public int compareTo(Demo12Person p) {
return p.name.compareTo(this.name); //姓名降序(字典序) -> [(n=人类3,a=5), (n=人类2,a=2), (n=人类1,a=3)]
//return this.name.compareTo(p.name);//姓名升序(字典序) -> [(n=人类1,a=3), (n=人类2,a=2), (n=人类3,a=5)]
}
}
public class Demo12LambdaTreeSet {
public static void main(String[] args) {
TreeSet<Demo12Person> set = new TreeSet<>();
set.add(new Demo12Person("人类3", 5));
set.add(new Demo12Person("人类1", 3));
set.add(new Demo12Person("人类1", 2));//姓名重复、年龄不重复
set.add(new Demo12Person("人类2", 2));//年龄重复、姓名不重复
//Lambda表达式:姓名排序
System.out.println(set);
}
}
案例2.4 - TreeSet集合排序(TreeSet - 年龄排序)
a.TreeSet中的对象实现排序:
1. TreeSet的带参构造,指定排序规则(年龄age)
public class Demo13LambdaTreeSet {
static TreeSet<Demo13Person> set = new TreeSet<>();
public static void main(String[] args) {
set = new TreeSet<>((o1, o2) -> o2.age - o1.age); //年龄降序 -> [(n=人类3,a=5), (n=人类1,a=3), (n=人类1,a=2)]
//set = new TreeSet<>((o1, o2) -> o2.age.compareTo(o1.age)); //年龄降序 -> [(n=人类3,a=5), (n=人类1,a=3), (n=人类1,a=2)]
//set = new TreeSet<>((o1, o2) -> o1.age - o2.age); //年龄升序 -> [(n=人类1,a=2), (n=人类1,a=3), (n=人类3,a=5)]
//set = new TreeSet<>((o1, o2) -> o1.age.compareTo(o2.age)); //年龄升序 -> [(n=人类1,a=2), (n=人类1,a=3), (n=人类3,a=5)]
...
}
}
2. 按照年龄排序时,也就表示TreeSet<Person>中年龄相等的元素会去重。
案例代码十三 TreeSet集合排序(TreeSet - 年龄排序)
b.类列表:
1. Demo13Person -> 实体类,有两个属性(name,age)
2. Demo13LambdaTreeSet -> 测试类
package com.groupies.jdk8.day01;
import java.util.TreeSet;
/**
* @author GroupiesM
* @date 2021/11/16
* @introduction 案例2.4 - TreeSet集合排序(TreeSet - 年龄排序)
*/
class Demo13Person {
public String name;
public Integer age;
public Demo13Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "(n=" + name + ",a=" + age + ")";
}
}
public class Demo13LambdaTreeSet {
static TreeSet<Demo13Person> set = new TreeSet<>();
public static void main(String[] args) {
set = new TreeSet<>((o1, o2) -> o2.age - o1.age); //年龄降序 -> [(n=人类3,a=5), (n=人类1,a=3), (n=人类1,a=2)]
//set = new TreeSet<>((o1, o2) -> o2.age.compareTo(o1.age)); //年龄降序 -> [(n=人类3,a=5), (n=人类1,a=3), (n=人类1,a=2)]
//set = new TreeSet<>((o1, o2) -> o1.age - o2.age); //年龄升序 -> [(n=人类1,a=2), (n=人类1,a=3), (n=人类3,a=5)]
//set = new TreeSet<>((o1, o2) -> o1.age.compareTo(o2.age)); //年龄升序 -> [(n=人类1,a=2), (n=人类1,a=3), (n=人类3,a=5)]
set.add(new Demo13Person("人类3", 5));
set.add(new Demo13Person("人类1", 3));
set.add(new Demo13Person("人类1", 2));//姓名重复、年龄不重复
set.add(new Demo13Person("人类2", 2));//年龄重复、姓名不重复
//Lambda表达式:年龄排序
System.out.println(set);
}
}
案例2.5 - TreeSet集合排序(TreeSet - 姓名排序)
a.TreeSet中的对象实现排序:
1. TreeSet的带参构造,指定排序规则(姓名name)
public class Demo14LambdaTreeSet {
static TreeSet<Demo14Person> set = new TreeSet<>();
public static void main(String[] args) {
set = new TreeSet<>((o1, o2) -> o2.name.compareTo(o1.name)); //姓名降序 -> [(n=人类3,a=5), (n=人类2,a=2), (n=人类1,a=3)]
//set = new TreeSet<>((o1, o2) -> o1.name.compareTo(o2.name)); //姓名升序 -> [(n=人类1,a=3), (n=人类2,a=2), (n=人类3,a=5)]
...
}
}
2. 按照姓名排序时,也就表示TreeSet<Person>中姓名相等的元素会去重。
案例代码十四 TreeSet集合排序(TreeSet - 姓名排序)
b.类列表:
1. Demo14Person -> 实体类,有两个属性(name,age)
2. Demo14LambdaTreeSet -> 测试类
package com.groupies.jdk8.day01;
import java.util.TreeSet;
/**
* @author GroupiesM
* @date 2021/11/16
* @introduction 案例2.5 - TreeSet集合排序(TreeSet - 姓名排序)
*/
class Demo14Person {
public String name;
public Integer age;
public Demo14Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "(n=" + name + ",a=" + age + ")";
}
}
public class Demo14LambdaTreeSet {
static TreeSet<Demo14Person> set = new TreeSet<>();
public static void main(String[] args) {
set = new TreeSet<>((o1, o2) -> o2.name.compareTo(o1.name)); //姓名降序 -> [(n=人类3,a=5), (n=人类2,a=2), (n=人类1,a=3)]
//set = new TreeSet<>((o1, o2) -> o1.name.compareTo(o2.name)); //姓名升序 -> [(n=人类1,a=3), (n=人类2,a=2), (n=人类3,a=5)]
set.add(new Demo14Person("人类3", 5));
set.add(new Demo14Person("人类1", 3));
set.add(new Demo14Person("人类1", 2));//姓名重复、年龄不重复
set.add(new Demo14Person("人类2", 2));//年龄重复、姓名不重复
//Lambda表达式:年龄排序
System.out.println(set);
}
}
🌟 TreeSet集合排序(Comparable/Comparator - 原理)
a.Comparable - 原理:
1. 案例2.1 - 案例2.5 中不论是哪种方式实现排序,都涉及到了Comparable接口,只有了解这个接口的实现原理,才能实现更多玩法,比如:不排序,不去重,多条件排序(先按年龄降序,再按姓名升序)
2. 要搞懂原理,先来看一下Comparable源码,将源码中注释的一坨英语扔到有道词典,会得到一堆狗屁不通的翻译。总结一下大概意思是:compareTo会返回一个数字来表示比较结果,入参大于原参则返回正整数,相等返回0,小于返回负整数;
将此对象与指定的对象进行顺序比较。当此对象小于、等于或大于指定对象时,返回一个负整数、零或正整数。实现者必须确保sgn(x.c areto (y)) == -sgn(y.c areto (x))对于所有的x和y。(这意味着x.c areto (y)必须在y.c areto (x)抛出异常时抛出异常。)实现者还必须确保关系是可传递的:(x.c areto (y)>0 && y.c areto (z)>0)暗指x.c areto (z)>0。最后,实现器必须确保x.c areto (y)==0意味着sgn(x.c areto (z)) == sgn(y.c areto (z)),对于所有的z。强烈建议,但不是严格要求(x.c areto (y)==0) == (x.c areto (y))。一般来说,任何实现Comparable接口并违反此条件的类都应该清楚地表明这一事实。推荐的语言是“注意:该类具有与equals不一致的自然顺序”。在前面的描述中,符号sgn(表达式)指定了数学上的sgum函数,根据表达式的值是负、零还是正,定义该函数返回-1、0或1中的一个。
参数:要比较的对象。
返回:负整数、零或正整数,因为该对象小于、等于或大于指定的对象。
抛出:NullPointerException——如果指定的对象是null ClassCastException——如果指定的对象的类型阻止它与该对象进行比较。
3. 需要注意:以上案例中,排序涉及到两个接口
案例2.2、案例2.3中使用的是Comparable接口,public int compareTo(T o);
案例2.4、案例2.5中使用的是Comparator接口,int compare(T o1, T o2);
4. 两个接口功能大同小异,都可以实现需求;但是对于一些非自定义类(比如Integer),不能修改jdk源码,就只能使用Comparator接口了;
5. Integer、String这些类也有compareTo方法,例如下面实例中的compareTo方法是比较两个对象的name属性,并返回一个数字,并通过Lambda表达式将String类的compareTo的实现逻辑作为参数传入TreeSet构造器;而Integer类型除了使用compareTo方法,还可以通过直接相减判断大小关系(案例2.2、案例2.4)
也就是在Lambda表达式 - 简介中所说的定义:Lambda是JAVA 8添加的一个新的特性,功能上类似于匿名函数。也叫做闭包,允许把函数作为参数传递进方法中
TreeSet<Person> set = new TreeSet<>((o1, o2) -> o2.name.compareTo(o1.name));
6. jdk会根据集合的情况,判断使用哪一种排序算法(例如堆排序,快速排序等),再以comepareTo返回值(正整数=>大于、负整数=>小于、0等于)作为依据,对TreeSet进行排序
7. 重写compareTo方法时,加入一些逻辑判断,控制返回值即可解锁更多玩法
案例2.6 - TreeSet - freeStyle(姓名排序、不去重)
a.TreeSet - freeStyle思路:
1. 想要实现不去重,就需要compareTo方法返回值不为0
2. 核心代码逻辑如下(主要是将返回值为0的情况去掉,就不会出现去重)
TreeSet<Demo12Person> set = new TreeSet<>(
(o1, o2) -> o2.name.compareTo(o1.name) == 0 ? 1 : o2.name.compareTo(o1.name));
3. 上面返回固定值1也可以替换为其他非0数字,比如-5,20
案例代码十五 TreeSet - freeStyle(姓名排序、不去重)
b.类列表:
1. Demo15Person -> 实体类,有两个属性(name,age)
2. Demo15LambdaTreeSet -> 测试类
package com.groupies.jdk8.day01;
import java.util.TreeSet;
/**
* @author GroupiesM
* @date 2021/11/16
* @introduction 案例2.6 - TreeSet - freeStyle(姓名排序、不去重)
*/
class Demo15Person {
public String name;
public Integer age;
public Demo15Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "(n=" + name + ",a=" + age + ")";
}
}
public class Demo15LambdaTreeSet {
public static void main(String[] args) {
//姓名降序,不去重 -> [(n=人类3,a=5), (n=人类2,a=2), (n=人类1,a=3), (n=人类1,a=2)]
TreeSet<Demo15Person> set = new TreeSet<>((o1, o2) -> o2.name.compareTo(o1.name) == 0 ? 1 : o2.name.compareTo(o1.name));
set.add(new Demo15Person("人类3", 5));
set.add(new Demo15Person("人类1", 3));
set.add(new Demo15Person("人类1", 2));//姓名重复、年龄不重复
set.add(new Demo15Person("人类2", 2));//年龄重复、姓名不重复
System.out.println(set);
}
}
案例2.7 - TreeSet - freeStyle(不排序、姓名去重)
a.TreeSet - freeStyle思路:
1. 想要实现不去重,只要在compareTo返回值不为0时,固定返回其他值即可
2. 核心代码逻辑如下(返回0表示去重,返回固定值1表示位置不变)
TreeSet<Demo16Person> set = new TreeSet<>(
(o1, o2) -> o2.name.compareTo(o1.name) == 0 ? 0 : 1);
3. 上面返回固定值1也可以替换为其他非0数字,比如-5,20
案例代码十六 TreeSet集合排序(不排序、姓名去重)
b.类列表:
1. Demo16Person -> 实体类,有两个属性(name,age)
2. Demo16LambdaTreeSet -> 测试类
package com.groupies.jdk8.day01;
import java.util.TreeSet;
/**
* @author GroupiesM
* @date 2021/11/16
* @introduction 案例2.7 - TreeSet - freeStyle(不排序、姓名去重)
*/
class Demo16Person {
public String name;
public Integer age;
public Demo16Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "(n=" + name + ",a=" + age + ")";
}
}
public class Demo16LambdaTreeSet {
public static void main(String[] args) {
//不排序、姓名去重 -> [(n=人类3,a=5), (n=人类1,a=3), (n=人类2,a=2)]
TreeSet<Demo16Person> set = new TreeSet<>((o1, o2) -> o2.name.compareTo(o1.name) == 0 ? 0 : 1);
set.add(new Demo16Person("人类3", 5));
set.add(new Demo16Person("人类1", 3));
set.add(new Demo16Person("人类1", 2));//此条数据被去重过滤掉
set.add(new Demo16Person("人类2", 2));//
System.out.println(set);
}
}
案例2.8 - TreeSet - freeStyle(多条件排序)
a.排序规则:
1.先按照年龄降序
2.如果年龄相同,按照姓名升序
3.如果年龄和姓名都一致,则去重
--用sql表示如下
select distinct
name,
age
from
Person
order by
age desc,
name asc
b.TreeSet - freeStyle思路:
1. 直接看代码
TreeSet<Demo16Person> set = new TreeSet<>(
(o1, o2) -> o2.name.compareTo(o1.name) == 0 ? 0 : 1);
2. 上面返回固定值1也可以替换为其他非0数字,比如-5,20
案例代码十七 TreeSet集合排序(多条件排序)
c.类列表:
1. Demo17Person -> 实体类,有两个属性(name,age)
2. Demo17LambdaTreeSet -> 测试类
package com.groupies.jdk8.day01;
import java.util.TreeSet;
/**
* @author GroupiesM
* @date 2021/11/16
* @introduction 案例2.8 - TreeSet - freeStyle(多条件排序)
*
* 排序规则:
* 1.先按照年龄降序
* 2.如果年龄相同,按照姓名升序
* 3.如果年龄和姓名都一致,则去重
*/
class Demo17Person {
public String name;
public Integer age;
public Demo17Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "(n=" + name + ",a=" + age + ")";
}
}
public class Demo17LambdaTreeSet {
public static void main(String[] args) {
//不排序、姓名去重 -> [(n=人类3,a=5), (n=人类1,a=3), (n=人类2,a=2)]
TreeSet<Demo17Person> set = new TreeSet<>(
(p1, p2) -> {
int ageDesc = p2.age.compareTo(p1.age);//年龄降序
int nameAsc = p1.name.compareTo(p2.name);//姓名升序
//ageDesc == 0表示年龄相等
if (ageDesc == 0) {
//表示年龄和姓名都相等时,去重
if (nameAsc == 0) return 0;
return nameAsc;
}
return ageDesc;
}
);
set.add(new Demo17Person("人类1", 1));
set.add(new Demo17Person("人类1", 1));//姓名和年龄都重复,被去重
set.add(new Demo17Person("人类2", 2));
set.add(new Demo17Person("人类3", 4));
set.add(new Demo17Person("人类3", 3));
set.add(new Demo17Person("人类1", 3));
set.add(new Demo17Person("人类5", 3));
//[(n=人类3,a=4), (n=人类1,a=3), (n=人类3,a=3),
//(n=人类5,a=3), (n=人类2,a=2), (n=人类1,a=1)]
System.out.println(set);
}
}
案例2.9 - TreeSet - freeStyle(测试 - 同时排序)
a.测试目的:
1. 测试TreeSet和Comparable都指定排序规则时,jdk会如何处理
b.TreeSet - freeStyle思路:
1. 在Person类指定姓名降序,TreeSet构造器指定年龄降序,查看执行结果
c.测试结果:
1. 以TreeSet构造器中指定的排序规则为准(年龄降序,年龄去重),Person类中指定的逻辑并未执行
d.测试结果:
1. TreeSet构造器中和
案例代码十八 TreeSet集合排序(多条件排序)
e.类列表:
1. Demo18Person -> 实体类,有两个属性(name,age),实现了Comparable接口,姓名降序
2. Demo18LambdaTreeSet -> 测试类
package com.groupies.jdk8.day01;
import java.util.TreeSet;
/**
* @author GroupiesM
* @date 2021/11/16
* @introduction 案例2.9 - TreeSet - freeStyle(测试 - 同时排序)
* 测试TreeSet和Comparable都指定排序规则时,jdk会如何处理
*/
class Demo18Person implements Comparable<Demo18Person> {
public String name;
public Integer age;
public Demo18Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "(n=" + name + ",a=" + age + ")";
}
@Override
public int compareTo(Demo18Person p) {
return p.name.compareTo(this.name);//姓名降序
}
}
public class Demo18LambdaTreeSet {
public static void main(String[] args) {
TreeSet<Demo18Person> set = new TreeSet<>((p1, p2) -> p2.age - p1.age);//年龄降序
set.add(new Demo18Person("人类1", 1));
set.add(new Demo18Person("人类1", 1));
set.add(new Demo18Person("人类2", 2));
set.add(new Demo18Person("人类3", 4));
set.add(new Demo18Person("人类3", 3));
set.add(new Demo18Person("人类1", 3));
set.add(new Demo18Person("人类5", 3));
//[(n=人类3,a=4), (n=人类3,a=3), (n=人类2,a=2), (n=人类1,a=1)]
System.out.println(set);
}
}
5.3 案例3 - ArrayList.forEach遍历
a.需求:
1. ArrayList<Integer>中,在控制台打印所有偶数元素(for循环两种方式省略不写)
b.forEach()源码分析:
1. ArrayList.forEach如要传入一个Consumer接口的实现,部分源码如下:
2. 注意核心逻辑这部分 action.accept(elementData[i]);
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
....
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);//将集合中的每一个元素都带入到Consumer类accept方法中
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
...
}
2. Consumer接口部分源码如下:
2.1 Cosumer接口属于本文中 6.系统内置函数式接口 的 消费型接口
2.2 Cosumer接口被 @FunctionalInterface 修饰,那么就可以通过Lambda表达式来实现这个接口
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
...
}
案例3.1 - Iterator迭代器
a.需求:
1. ArrayList<Integer>中,通过Iterator迭代器的方式,在控制台打印所有偶数元素
案例代码十九 Iterator迭代器
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.Collections;
import java.util.ListIterator;
/**
* @author GroupiesM
* @date 2021/11/17
* @introduction 案例3.1 - Iterator迭代器
* 需求: ArrayList<Integer>中,在控制台打印所有偶数元素
*/
public class Demo19LambdaForeach {
static ArrayList<Integer> list = new ArrayList<>();
public static void main(String[] args) {
Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
ListIterator<Integer> it = list.listIterator();
while (it.hasNext()) {
System.out.print(it.next() + " ");
}
}
}
案例3.2 - forEach - 匿名内部类
a.需求:
1. ArrayList<Integer>中,通过匿名内部类的方式,在控制台打印所有偶数元素 -> 2 4 6 8 0
案例代码二十 forEach - 匿名内部类
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Consumer;
/**
* @author GroupiesM
* @date 2021/11/17
* @introduction 案例3.2 - forEach - 匿名内部类
* 需求: ArrayList<Integer>中,在控制台打印所有偶数元素
*/
public class Demo20LambdaForeach {
static ArrayList<Integer> list = new ArrayList<>();
public static void main(String[] args) {
Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer i) {
System.out.print(i % 2 == 0 ? i + " " : "");
}
});
}
}
案例3.3 - forEach - Lambda表达式
a.需求:
1. ArrayList<Integer>中,通过Lambda表达式的方式,在控制台打印所有偶数元素 -> 2 4 6 8 0
案例代码二十一 forEach - Lambda表达式
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Consumer;
/**
* @author GroupiesM
* @date 2021/11/17
* @introduction 案例3.3 - forEach - Lambda表达式
* 需求: ArrayList<Integer>中,在控制台打印所有偶数元素
*/
public class Demo21LambdaForeach {
static ArrayList<Integer> list = new ArrayList<>();
public static void main(String[] args) {
Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
Consumer<Integer> consumer = ele -> System.out.print((ele % 2 == 0 ? ele + " " : ""));
list.forEach(consumer);
System.out.println("\n这两行代码合并为下面一行");
list.forEach(ele -> System.out.print((ele % 2 == 0 ? ele + " " : "")));
}
}
案例3.4 - forEach - Lambda表达式::(方法引用)❌
a.需求:
1. ArrayList<Integer>中,通过Lambda表达式::(方法引用)的方式,在控制台打印所有偶数元素 -> 2 4 6 8 0;
2. ❌ 表示,方法引用无法实现打印所有偶数元素的需求
案例代码二十二 forEach - Lambda表达式::(方法引用)
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Consumer;
/**
* @author GroupiesM
* @date 2021/11/17
* @introduction 案例3 - ArrayList.forEach遍历
* 需求: ArrayList<Integer>中,在控制台打印所有偶数元素
*/
public class Demo22LambdaForeach {
static ArrayList<Integer> list = new ArrayList<>();
public static void main(String[] args) {
Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
Consumer<Integer> consumer = System.out::print;
list.forEach(consumer);
System.out.println("\n这两行代码合并为下面一行");
list.forEach(System.out::print);
}
}
5.4 案例4 - ArrayList.removeIf条件删除
a.需求:
1. ArrayList<Person>中,删除年龄age大于4的Person元素(for循环两种方式省略不写)
b.removeIf()源码分析:
1. ArrayList.removeIf如要传入一个Predicate接口的实现,部分源码如下:
2. 注意核心逻辑这部分 if (filter.test(element)) ,Predicate的接口实现会返回一个boolean值(符合条件则返回true,并通过if逻辑)
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
....
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {//核心逻辑
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
...
}
2. Predicate接口部分源码如下:
2.1 Predicate接口属于本文中 6.系统内置函数式接口 的 断言型接口
2.2 Predicate接口被 @FunctionalInterface 修饰,那么就可以通过Lambda表达式来实现这个接口
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
...
}
案例4.1 - Iterator迭代器
a.需求:
1. ArrayList<Person>中,通过Iterator迭代器的方式,删除年龄age大于4的Person元素
-> (name=人类1,age=3)(name=人类2,age=4)
案例代码二十三 Iterator迭代器
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.ListIterator;
/**
* @author GroupiesM
* @date 2021/11/17
* @introduction 案例4.1 - Iterator迭代器
* 需求:删除集合中年龄>4岁的People元素
*/
class Demo23Person {
public String name;
public int age;
public Demo23Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {return "(name=" + name + ",age=" + age + ")";}
}
public class Demo23LambdaRemoveIf {
public static void main(String[] args) {
ArrayList<Demo23Person> list = new ArrayList<>();
list.add(new Demo23Person("人类1", 3));
list.add(new Demo23Person("人类2", 4));
list.add(new Demo23Person("人类3", 5));
list.add(new Demo23Person("人类4", 6));
ListIterator<Demo23Person> it = list.listIterator();
while (it.hasNext()) {
Demo23Person ele = it.next();
if (ele.age > 4) {
it.remove();
}
}
list.forEach(System.out::print);
}
}
案例4.2 - removeIf - 匿名内部类
a.需求:
1. ArrayList<Person>中,通过匿名内部类的方式,删除年龄age大于4的Person元素
-> (name=人类1,age=3)(name=人类2,age=4)
案例代码二十四 removeIf - 匿名内部类
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.function.Predicate;
/**
* @author GroupiesM
* @date 2021/11/17
* @introduction 案例4.2 - removeIf - 匿名内部类
* 需求:删除集合中年龄>4岁的元素
*/
class Demo24Person {
public String name;
public int age;
public Demo24Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "(name=" + name + ",age=" + age + ")";
}
}
public class Demo24LambdaRemoveIf {
public static void main(String[] args) {
ArrayList<Demo24Person> list = new ArrayList<>();
list.add(new Demo24Person("人类1", 3));
list.add(new Demo24Person("人类2", 4));
list.add(new Demo24Person("人类3", 5));
list.add(new Demo24Person("人类4", 6));
list.removeIf(new Predicate<Demo24Person>() {
@Override
public boolean test(Demo24Person p) {
return p.age > 4;
}
});
list.forEach(System.out::print);
}
}
案例4.3 - removeIf - Lambda表达式
a.需求:
1. ArrayList<Person>中,通过Lambda表达式的方式,删除年龄age大于4的Person元素
-> (name=人类1,age=3)(name=人类2,age=4)
案例代码二十五 removeIf - Lambda表达式
package com.groupies.jdk8.day01;
import java.util.ArrayList;
/**
* @author GroupiesM
* @date 2021/11/17
* @introduction 案例4.3 - removeIf - Lambda表达式
* 需求:删除集合中年龄>4岁的元素
*/
class Demo25Person {
public String name;
public int age;
public Demo25Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {return "(name=" + name + ",age=" + age + ")";}
}
public class Demo25LambdaRemoveIf {
public static void main(String[] args) {
ArrayList<Demo25Person> list = new ArrayList<>();
list.add(new Demo25Person("人类1", 3));
list.add(new Demo25Person("人类2", 4));
list.add(new Demo25Person("人类3", 5));
list.add(new Demo25Person("人类4", 6));
list.removeIf(p -> p.age > 4);
list.forEach(System.out::print);
}
}
5.5 案例5 - 多线程
a.需求:
1. 线程实例化,并且在控制台打印每个线程的名称
2. 采用三种方式(a. 实现类,b. 匿名内部类,c. Lambda表达式)
3. 参考 -> 多线程
案例代码二十六 多线程
package com.groupies.jdk8.day01;
/**
* @author GroupiesM
* @date 2021/11/29
* @introduction
*/
public class Demo26LambdaThread {
public static void main(String[] args) {
//a.实现类 获取到线程名称:Thread-0,子线程
new Thread(new Demo26myRunnable()).start();
//b.匿名内部类 获取到线程名称:Thread-1,子线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("获取到线程名称:" + Thread.currentThread().getName() + ",子线程");
}
}).start();
//c.Lambda表达式 获取到线程名称:MyThread-2,子线程
new Thread(() -> System.out.println("获取到线程名称:" + Thread.currentThread().getName() + ",子线程"),"MyThread-2").start();
}
}
/**
* Runnable实现类
*/
class Demo26myRunnable implements Runnable {
@Override
public void run() {System.out.println("获取到线程名称:" + Thread.currentThread().getName() + ",子线程");}
}
5.6 案例6 - Stream流
a.简介:
1.stream流思想:类似于工厂的生产流水线,通过多个工序对原材料进行加工,形成商品;
2. Stream常用方法 (T表示泛型)
id | 方法名 | 方法作用 | 请求参数 | 返回值类型 | 操作类型 |
---|---|---|---|---|---|
1 | 创建流的六种方式 | 创建 | |||
1.1 | stream | 所有 Collection 接口下的实现类(list,set,vector…)都可以通过此方法来获取stream串行流 | - | Stream<E> | 创建 |
1.2 | parallelStream | 所有 Collection 接口下的实现类(list,set,vector…)都可以通过此方法来获取stream并行流 | - | Stream<E> | 创建 |
1.3 | Arrays.stream | 静态方法,根据数组元素创建对应Stream流 | T[ ] | Stream<T> | 创建 |
1.4 | Stream.of | 静态方法,根据任意元素创建对应Stream流 T…:请求参数属于可变长参数,详情查看 => 博客 | T… | Stream<T> | 创建 |
1.5 | Stream.iterate | 静态方法,创建无限流(迭代) seed: 初始值 f : function->对初始值的迭代函数 | T seed, UnaryOperator<T> f | Stream<T> | 创建 |
1.6 | Stream.generate | 静态方法,创建无限流(生成) Supplier属于系统内置函数式接口 -> 供给型接口 | Supplier<T> | Stream<T> | 创建 |
2 | filter | 过滤,符合条件的才会继续保留在Stream流中 | Predicate<T> | Stream | 中间 |
3 | limit | 取用前几个 | long | Stream<T> | 中间 |
4 | skip | 跳过前几个 | long | Stream<T> | 中间 |
5 | map | 转换 将Stream流中的元素转为任意类型 Stream<T> -> Stream<R> | Function<T,R> | Stream<R> | 中间 |
6 | concat | 静态方法,合并2个Stream流成为一个Stream流 | Stream<T>, Stream<T> | Stream<T> | 中间 |
7 | sorted | 排序 | - | Stream<T> | 中间 |
8 | distinct | 去重 distinct依赖Object的equals方法来判断是否是相同对象,如果需按照某元素去重时,注意重新定义equals方法 | - | Stream<T> | 中间 |
9 | flatMap | 将流展开 | Function<T,R> | Stream<R> | 中间 |
10 | forEach | 逐一处理 | Consumer<T> | void | 终止 |
11 | count | 统计个数 | - | long | 终止操作 |
12 | collect | 参数 supplier :生成目标类型实例的方法,代表着目标容器是什么; 参数 accumulator :将操作的目标数据填充到supplier 生成的目标类型实例中去的方法,代表着如何将元素添加到容器中; 参数 combiner :将多个supplier 生成的实例整合到一起的方法,代表着规约操作,将多个结果合并。 假如我们需要将保存有多个Person实例的list做一个转变,变为以id为key,value为person的Map的话,那就可以使用这个方法了: Map<Long,Person> personMap = list.stream() .collect(Maps::newHashMap, (map,p)>map.put(p.getId(),p), Map::putAll); 或: Map<Long,Person> personMap = list.stream() .collect(() -> new HashMap<>(), (map ,p) ->map.put(p.getId(),p), (m ,n) -> m.putAll(n)); | Supplier<R>supplier, BiConsumer<R,T>accumulator, BiConsumer<R,R>combiner | R | 终止 |
collect | 使用 Collectors(java.util.stream.Collectors)来进行各种 reduction 操作 | Collector<T, A, R> | R | 终止 | |
13 | max和min | 最大(小)值 Comparator<T> :比较器,指定比较大小的方式 Optional<T> :返回泛型T,包装一层Optional判空,这样就可以允许返回空值 | Comparator<T> | Optional<T> | 终止 |
14 | find | 查找 1. findFirst:如果一个集合数据是有序的,而且你要查找符合条件的第一条数据。这时用findFirst是比较合适的。 2. findAny:返回任意一条数据,不关心顺序。该方法不能保证获取的一定是流中的第一条数据。且在并行流中,findAny限制更少。 | - | Optional<T> | 终止 |
15 | match | 匹配 1. allMatch 全部匹配,则返回true 2. anymatch 任一一条匹配,则返回true 3. nonematch 全部不匹配,则返回true | Predicate<T> | boolean | 终止 |
16 | reduce(accumulator) | 聚合 聚合的含义就是将多个值经过特定计算之后得到单个值 参数1. accumulator 计算方式 | BinaryOperator<T>accumulator | Optional<T> | 终止 |
reduce(accumulator, combiner) | accumulator 计算方式 combiner 初始值 | T identity, BinaryOperator<T>accumulator | T | 终止 | |
reduce(identity, accumulator,combiner) | 详情查看=>博客 identity 初始值 accumulator 计算方式 combiner 并行计算下 合并各个线程的计算结果 | U identity, BiFunction<U,T,U>accumulator BinaryOperator<U>combiner | Optional<U> | 终止 | |
map + reduce组合 | map:实现数据类型的转换,符合reduce对数据的要求 reduce:实现数据的处理 | long | Optional<T> | 终止 | |
终止操作 : 返回值不再是Stream类型,后续不再支持链式调用 | |||||
非终止操作 : 返回值再是Stream类型,后续支持链式调用 |
3. 创建Stream的两种方式:串行流stream() & 并行流parallelStream()
串行流 : 适合存在线程安全问题、阻塞任务、重量级任务,以及需要使用同一事务的逻辑。
并行流 : 适合没有线程安全问题、较单纯的数据处理任务。
4. Stream流如果不调用终结方法,非终结方法中的内容是不会执行的
//举例:这里的filter方法中的内容不会打印
ArrayList<String> list = new ArrayList<>();
list.stream().filter(ele -> {
System.out.println("正在执行filter方法");
return ele.startsWith("a");
});
5. Stream流一旦调用终结方法,就不能再使用了
//举例:流调用了一次forEach(终结)方法,就不能再次调用了
//数据准备
Stream<Integer> stream = Stream.of(1, 2, 3, 4);
//34
stream.filter(e->e>2).forEach(System.out::print);
//流已被操作或关闭
//java.lang.IllegalStateException: stream has already been operated upon or closed
stream.forEach(System.out::println);
案例6.1 - 获取stream
方式一/二:通过Collection接口:
java.util.Collection接口中加入了default方法stream()和parallelStream(),也就是说Collection接口下所有的实现都可以通过stream(parallelStream)方法来获取stream流(串行流/并行流)
ArrayList<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
Stream<String> stream2 = list.parallelStream();
方式三:通过Stream.of():
在实际开发中经常会使用到数组中的数据,而数组没有默认方法,所以Stream接口中提供了静态方法of
String[] arr = {"aa", "bb", "cc"};
Stream<String> stream1 = Arrays.stream(arr);
Stream<Object> stream2 = Stream.of(1, "a");
方式四:通过Arrays.stream():
除了Stream.of,Arrays类也提供了静态方法stream,但Arrays.stream方法接收参数只能是数组
String[] arr = {"aa", "bb", "cc"};
Stream<String> stream = Arrays.stream(arr);
方式五:通过Stream.iterate(): 迭代 无限流
除了Stream.of,Arrays类也提供了静态方法stream
Stream<Integer> stream = Stream.iterate(0, x -> x + 1);
stream.limit(4).filter(x -> x % 2 == 0).forEach(System.out::print);
方式六:通过Stream.generate(): 生成无限流
Stream<Object> stream = Stream.generate((Supplier<Object>) () -> Math.random());
stream.limit(4).forEach(System.out::println);
案例6.2 - List -> Set
案例代码二十八 Stream流List -> Set (stream.collect)
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author GroupiesM
* @date 2021/12/14 Stream流 List -> Set (stream.collect)
* @introduction Collectors.toSet()
*/
public class Demo28LambdaStream {
public static void main(String[] args) {
//数据准备
ArrayList<People> list = new ArrayList<>();
list.add(new People("张一三", 5));
list.add(new People("赵四", 6));
list.add(new People("王五", 5));
list.add(new People("张一三", 5));
//方式1.Stream流
/*
People{name='张一三, age=5}
People{name='张一三, age=5}
People{name='王五, age=5}
People{name='赵四, age=6}
*/
Set<People> setCollect1 = list.stream().collect(Collectors.toSet());
setCollect1.forEach(System.out::println);
System.out.println("...");
//方式2.HashSet构造器
/*
People{name='张一三, age=5}
People{name='张一三, age=5}
People{name='王五, age=5}
People{name='赵四, age=6}
*/
Set<People> setCollect2 = new HashSet<>(list);
setCollect2.forEach(System.out::println);
}
/**
* 实体类
*/
static class People{
String name;
Integer age;
public People(String name, Integer age) {this.name = name;this.age = age;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Integer getAge() {return age;}
public void setAge(Integer age) {this.age = age;}
@Override
public String toString() {return "People{name='" + name + ", age=" + age + '}';}
}
}
案例6.3 - List -> TreeSet
案例代码二十九 Stream流List -> TreeSet (stream.collect)
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.TreeSet;
import java.util.stream.Collectors;
/**
* @author GroupiesM
* @date 2021/12/14 Stream流List -> TreeSet (stream.collect)
* @introduction Collectors.toCollection()
*/
public class Demo29LambdaStream {
public static void main(String[] args) {
//数据准备
ArrayList<People> list = new ArrayList<>();
list.add(new People("张一三", 5));
list.add(new People("赵四", 6));
list.add(new People("王五", 5));
list.add(new People("张一三", 65));
list.add(new People("张一三", 65));
//方式1.Stream流 people实现了Comparable接口,要求年龄不能相同
/*
People{name='张一三, age=5}
People{name='赵四, age=6}
People{name='张一三, age=65}
*/
TreeSet<People> treeSetCollect1 = list.stream().collect(Collectors.toCollection(TreeSet::new));
treeSetCollect1.forEach(System.out::println);
System.out.println("...");
//方式2.TreeSet构造器 people实现了Comparable接口,要求年龄不能相同
/*
People{name='张一三, age=5}
People{name='赵四, age=6}
People{name='张一三, age=65}
*/
TreeSet<People> treeSetCollect2 = new TreeSet<>(list);
treeSetCollect2.forEach(System.out::println);
}
/**
* 实体类,实现了Comparable接口,按照年龄排序
*/
static class People implements Comparable<People> {
String name;
Integer age;
public People(String name, Integer age) {this.name = name;this.age = age;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Integer getAge() {return age;}
public void setAge(Integer age) {this.age = age;}
@Override
public String toString() {return "People{name='" + name + ", age=" + age + '}';}
@Override
public int compareTo(People o) {return this.age.compareTo(o.age);}
}
}
案例6.4 - List -> Map
案例代码三十 Stream流List -> Set (stream.collect)
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author GroupiesM
* @date 2021/12/14 Stream流list -> set (stream.collect)
* @introduction Collectors.toMap()
*/
public class Demo30LambdaStream {
public static void main(String[] args) {
//数据准备
ArrayList<People> list = new ArrayList<>();
list.add(new People("张一三", 5));
list.add(new People("赵四", 6));
list.add(new People("王五", 5));
list.add(new People("张一三", 65));
list.add(new People("张一三", 65));
//方式1.Stream流 Collectors.toMap(Function<? super T, ? extends K> keyMapper,Function<? super T, ? extends U> valueMapper)
/*
主键重复,报错,需要指定重复时如何取值 => Duplicate key People{name='zhangsan, age=65}
*/
Map<String, People> mapCollect1 = null;
try {
mapCollect1 = list.stream().collect(Collectors.toMap(People::getName, p -> p));
} catch (Exception e) {
e.printStackTrace();//java.lang.IllegalStateException: Duplicate key People{name='zhangsan, age=65}
}
//方式2.Stream流 Collectors.toMap(Function<? super T, ? extends K> keyMapper,Function<? super T, ? extends U> valueMapper)
/*
key=张三...value=People{name='张三, age=5}
key=王五...value=People{name='王五, age=5}
key=赵四...value=People{name='赵四, age=6}
*/
Map<String, People> mapCollect2 = list.stream().collect(Collectors.toMap(People::getName, Function.identity(), (p1, p2) -> p1));
mapCollect2.keySet().forEach((k) -> System.out.println("key=" + k + "...value=" + mapCollect2.get(k)));
System.out.println("....");
//方式3.Stream流
/*
key=张三...value=People{name='张三, age=5}
key=王五...value=People{name='王五, age=5}
key=赵四...value=People{name='赵四, age=6}
*/
Map<String, People> mapCollect3 = list.stream().collect(Collectors.toMap(People::getName, p -> p, (p1, p2) -> p1));
mapCollect2.keySet().forEach((k) -> System.out.println("key=" + k + "...value=" + mapCollect3.get(k)));
}
/**
* 实体类,实现了Comparable接口,按照年龄排序
*/
static class People{
String name;
Integer age;
public People(String name, Integer age) {this.name = name;this.age = age;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Integer getAge() {return age;}
public void setAge(Integer age) {this.age = age;}
@Override
public String toString() {return "People{name='" + name + ", age=" + age + '}';}
}
}
案例6.5 - List -> String(先排序)
案例代码三十一 Stream流List -> String (stream.collect)
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author GroupiesM
* @date 2021/12/14 Stream流list -> String (stream.collect)
* @introduction Collectors.joining()
*/
public class Demo31LambdaStream {
public static void main(String[] args) {
//数据准备
ArrayList<People> list = new ArrayList<>();
list.add(new People("张一三", 5));
list.add(new People("赵四", 7));
list.add(new People("王五", 6));
//list -> String
//方式1.Stream流 Collectors.joining() 无参
String name1 = list.stream()
.sorted(Comparator.comparing(People::getAge))//年龄排序
.map(People::getName)//映射
.collect(Collectors.joining());//转为数据集合
//张一三王五赵四
System.out.println(name1);
//方式2.Stream流 Collectors.joining(CharSequence delimiter)
String name2 = list.stream()
.sorted(Comparator.comparing(People::getAge))//年龄排序
.map(People::getName)//映射
.collect(Collectors.joining(","));//转为数据集合
//张一三,王五,赵四
System.out.println(name2);
//方式3.Stream流 Collectors.joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix)
String name3 = list.stream()
.sorted(Comparator.comparing(People::getAge))//年龄排序
.map(ele -> "[姓名:'"+ele.getName()+"', 年龄:"+ ele.getAge()+"]")//映射
.collect(Collectors.joining(",","{","}"));//转为数据集合
//{[姓名:'张一三', 年龄:5],[姓名:'王五', 年龄:6],[姓名:'赵四', 年龄:7]}
System.out.println(name3);
}
/**
* 实体类,实现了Comparable接口,按照年龄排序
*/
static class People{
String name;
Integer age;
public People(String name, Integer age) {this.name = name;this.age = age;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Integer getAge() {return age;}
public void setAge(Integer age) {this.age = age;}
@Override
public String toString() {return "People{name='" + name + ", age=" + age + '}';}
}
}
案例6.6 - List 元素属性求和
案例代码三十二 Stream流List 元素属性求和
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* @author GroupiesM
* @date 2021/12/14 List元素属性求和
* @introduction 年龄求和
*/
public class Demo32LambdaStream {
public static void main(String[] args) {
//数据准备
ArrayList<People> list = new ArrayList<>();
list.add(new People("张一三", 5));
list.add(new People("赵四", 6));
list.add(new People("王五", 5));
//方式1.lambda表达式
Integer sumAge1 = list.stream().map(p -> p.getAge()).reduce((a1, a2) -> a1 + a2).get();
//16
System.out.println(sumAge1);
//方式2.lambda表达式简写
Integer sumAge2 = list.stream().map(People::getAge).reduce(Integer::sum).get();
//16
System.out.println(sumAge2);
}
/**
* 实体类
*/
static class People{
String name;
Integer age;
public People(String name, Integer age) {this.name = name;this.age = age;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Integer getAge() {return age;}
public void setAge(Integer age) {this.age = age;}
@Override
public String toString() {return "People{name='" + name + ", age=" + age + '}';}
}
}
案例6.7 - List 最大(小)值
案例代码三十三 List 最大(小)值
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.Comparator;
/**
* @author GroupiesM
* @date 2021/12/15
* @introduction List 最大(小)值
* 找出年龄最大的人 People{name='赵四, age=66}
*/
public class Demo33LambdaStream {
public static void main(String[] args) {
//数据准备
ArrayList<People> list = new ArrayList<>();
list.add(new People("张一三", 5));
list.add(new People("赵四", 66));
list.add(new People("王五", 7));
//方式1.lambda表达式
People maxAge1 = list.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge())).get();
System.out.println(maxAge1);
//方式2.lambda表达式简写
People maxAge2 = list.stream().max(Comparator.comparing(People::getAge)).get();
System.out.println(maxAge2);
//方式3.lambda表达式反向比较
People maxAge3 = list.stream().min((p1, p2) -> p2.getAge().compareTo(p1.getAge())).get();
System.out.println(maxAge3);
}
/**
* 实体类
*/
static class People{
String name;
Integer age;
public People(String name, Integer age) {this.name = name;this.age = age;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Integer getAge() {return age;}
public void setAge(Integer age) {this.age = age;}
@Override
public String toString() {return "People{name='" + name + ", age=" + age + '}';}
}
}
案例6.8 - Integer -> String
案例代码三十四 Integer -> String
package com.groupies.jdk8.day01;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author GroupiesM
* @date 2021/12/17
* @introduction Integer -> String
*/
public class Demo34LambdaStream {
public static void main(String[] args) {
//数据准备
List<Integer> integers = Arrays.asList(1, 2, 3, 4);
//List<Integer> -> List<String>
List<String> list = integers.stream()
.map(s -> s.toString())//映射
.collect(Collectors.toList());//转为数据集合
}
}
案例6.9 - String转大(小)写
案例代码三十五 String转大(小)写
package com.groupies.jdk8.day01;
import java.util.Arrays;
import java.util.List;
/**
* @author GroupiesM
* @date 2021/12/17
* @introduction String转大写
*/
public class Demo35LambdaStream {
public static void main(String[] args) {
//数据准备
List<String> data = Arrays.asList("ZHANGSAN,", "zhaosi,", "wangwu");
//方式1: map.foreach -> ZHANGSAN,ZHAOSI,WANGWU
data.stream().map(String::toUpperCase).forEach(System.out::print);
System.out.println("");
//方式2: foreach -> zhangsan,zhaosi,wangwu
data.stream().forEach(s -> System.out.print(s.toLowerCase()));
}
}
案例6.10 - Stream.match (匹配)
案例代码三十六 Stream.match (匹配)
a.match匹配的三种方式:
1. allMatch 全部匹配,则返回 true
2. anymatch 任一一条匹配,则返回 true
3. nonematch 全部不匹配,则返回 true
package com.groupies.jdk8.day01;
import java.util.Arrays;
import java.util.List;
/**
* @author GroupiesM
* @date 2021/12/17
* @introduction match匹配
* 1. allMatch 全部匹配,则返回true
* 2. anymatch 任一一条匹配,则返回true
* 3. nonematch 全部不匹配,则返回true
*/
public class Demo36LambdaStream {
public static void main(String[] args) {
//数据准备
List<Integer> data = Arrays.asList(2, 4, 6, 8, 12);
//全部是偶数:true
boolean b1 = data.stream().allMatch(e -> e % 2 == 0);
System.out.println("全部是偶数:" + b1);
//任一一条大于10:true
boolean b2 = data.stream().anyMatch(e -> e > 10);
System.out.println("任一一条大于10:" + b2);
//没有一条小于3:false
boolean b3 = data.stream().noneMatch(e -> e < 3);
System.out.println("没有一条小于3:" + b3);
}
}
案例6.11 - Stream.filter (过滤)
案例代码三十七 Stream.filter (过滤)
/**
* @author GroupiesM
* @date 2021/12/17
* @introduction filter过滤器
*/
public class Demo37LambdaStream {
public static void main(String[] args) {
//数据准备
List<Integer> data = Arrays.asList(2, 4, 5, 6, 8, 9);
//过滤(filter)所有偶数,并打印
data.stream().filter(e -> e % 2 == 0).forEach(System.out::println);
}
}
案例6.12 - sorted(排序) + limit(前n)
案例代码三十八 sorted(排序) + limit(前n)
package com.groupies.jdk8.day01;
import java.util.ArrayList;
import java.util.Comparator;
/**
* @author GroupiesM
* @date 2021/12/17
* @introduction sorted(排序) + limit(前n)
*/
public class Demo38LambdaStream {
public static void main(String[] args) {
//数据准备
ArrayList<People> list = new ArrayList<>();
list.add(new People("张一三", 5));
list.add(new People("赵四", 300));
list.add(new People("王五", 15));
list.add(new People("张一三", 20));
//年龄最大的1个 -> age desc limit 1
//People{name='赵四, age=300}
list.stream().sorted((p1,p2)->p2.age.compareTo(p1.age)).limit(1).forEach(System.out::println);
System.out.println("...");
//年龄最小的2个 -> age asc limit 2
/*
People{name='张一三, age=5}
People{name='王五, age=15}
*/
list.stream().sorted(Comparator.comparing(p -> p.age)).limit(2).forEach(System.out::println);
}
/**
* 实体类
*/
static class People{
String name;
Integer age;
public People(String name, Integer age) {this.name = name;this.age = age;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Integer getAge() {return age;}
public void setAge(Integer age) {this.age = age;}
@Override
public String toString() {return "People{name='" + name + ", age=" + age + '}';}
}
}
6. 系统内置函数式接口
记住常用的4个主要接口,扩展接口了解即可
主要接口
id | 函数式接口 | 参数类型 | 返回类型 | 包含方法 |
---|---|---|---|---|
⭐️1 | Predicate<T> 断言型接口 | T | boolean | boolean test(T t) |
⭐️2 | Consumer<T> 消费型接口 | T | void | void accept(T t) |
⭐️3 | Function<T,R> 函数型接口 | T | R | R apply(T t) |
⭐️4 | Supplier<T> 供给型接口 | 无 | T | T get() |
一、Predicate扩展接口
id | 函数式接口 | 参数类型 | 返回类型 | 包含方法 |
---|---|---|---|---|
1.1 | BiPredicate<T,U> | T,U | boolean | boolean test(T t, U u) |
1.2 | IntPredicate | int | boolean | boolean test(int value) |
1.3 | LongPredicate | long | boolean | boolean test(long value) |
1.4 | DoublePredicate | double | boolean | boolean test(double value) |
二、Consumer扩展接口
id | 函数式接口 | 参数类型 | 返回类型 | 包含方法 |
---|---|---|---|---|
2.1 | BiConsumer<T,U,R> | T,U | void | void accept(T t, U u) |
2.2 | IntConsumer | int | void | void accept(int value) |
2.3 | LongConsumer | long | void | void accept(long value) |
2.4 | DoubleConsumer | double | void | void accept(double value) |
三、Function扩展接口 | ||||
id | 函数式接口 | 参数 类型 | 返回 类型 | 包含方法 |
:– | :– | :– | :– | :– |
3.1 | BiFunction<T,U,R> | T,U | R | R apply(T t, U u) |
3.2 | IntFunction<R> | int | R | R apply(int value) |
3.3 | LongFunction<R> | long | R | R apply(long value) |
3.4 | DoubleFunction<R> | double | R | R apply(double value) |
3.5 | IntToLongFunction | int | long | long applyAsLong(int value) |
3.6 | IntToDoubleFunction | int | double | double applyAsDouble(int value) |
3.7 | LongToIntFunction | long | int | int applyAsInt(long value) |
3.8 | LongToDoubleFunction | long | double | double applyAsDouble(long value) |
3.9 | DoubleToIntFunction | double | int | int applyAsInt(double value) |
3.10 | DoubleToLongFunction | double | long | long applyAsLong(double value) |
3.11 | BinaryOperator<T> extens BiFunction<T,U,R> | T,T | T | minBy maxBy |
3-.12 | UnaryOperator<T> extends Function<T, T> | T | T | static UnaryOperator identity() {return t -> t;} |
7. 闭包
闭包定义
闭包(closure)是一个函数,通常也被成为闭包函数或绑定函数,该函数运行在一个特定的环境中,该环境中定义了一些本地的变量,当该函数被调用时,仍可以使用这些变量。
7.1 闭包的概念
案例代码三十九 闭包的概念
a.案例说明
在案例三十九中在案例三十九中,A函数在调用完后(赋值给B),就应该被销毁,A中的局部变量num也应该被销毁。但是在调用B方法时还是能够获取到A中的局部变量。
这是因为闭包的机制,延长了变量num的生命周期。
package com.groupies.jdk8.day01;
import java.util.function.Supplier;
/**
* @author GroupiesM
* @date 2021/12/17
* @introduction 闭包的概念
*/
public class Demo39LambdaClosure {
public static void main(String[] args) {
Supplier<Integer> B = A();
Integer i = B.get();//调用函数B(),返回的是闭包函数A中num的值: 10
System.out.println(i);//10
}
private static Supplier<Integer> A() {
int num = 10;//局部变量
return () -> num;//定义一个闭包函数A,使用本地变量,并返回闭包函数
}
}
7.2 闭包与final
案例代码四十 闭包与final
a.案例说明
在案例四十中,对变量进行操作时,会提示以下信息
Variable used in lambda expression should be final or effectively final
在lambda表达式中使用的变量应该/实际上是被final修饰的
反之同理,一个变量是不能出现在闭包中的
结论:闭包中引用的变量,一定是常量
package com.groupies.jdk8.day01;
import java.util.function.Supplier;
/**
* @author GroupiesM
* @date 2021/12/17
* @introduction 闭包与final
*
*/
public class Demo40LambdaClosure {
public static void main(String[] args) {
//int num =10;由于在闭包中被引用,被延长了生命周期,在编译期会自动加上final修饰符
//int num = 10; => final int num = 10;
int num = 10;
Supplier<Integer> A = () -> num + 1;
System.out.println(A.get());//11
//Variable used in lambda expression should be final or effectively final
Supplier<Integer> C = () -> num++;
System.out.println(C.get());
}
}
8. lambda使用技巧
8.1 不知道怎么用lambda传参
首先根据提示,查看需要什么参数,例如图中filter方法需要一个Predicate对象
按照传统匿名内部类的方式,new一个Predicate对象
代码实现业务逻辑后,可以看到提示,匿名内部类可以简化为lambda表达式
alt+回车 => Replace with lambda
自动替换为lambda表达式形式
21/12/17
M