3.1_25 JavaSE-JDK8新特性 P1 【Lambda表达式】

相关链接


目录


1.Lambda表达式 - 简介


  1.什么是Lambda?
  Lambda是JAVA 8添加的一个新的特性,是一种基于匿名函数的更简洁的写法。也叫做闭包,允许把函数作为参数传递进方法中。这种思想来自于数学逻辑中的 λ演算

抽象类abstract接口interface(含有多个抽象方法)接口interface(含有单个抽象方法)
1.实现类
2.匿名内部类
3.Lambda表达式XX

—— 三种实现方式的适用范围对比

  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方法大括号{}: 如果方法体中只有一条语句,此时大括号可以省略
4return: 如果方法体中唯一的一条语句是一个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流思想:类似于工厂的生产流水线,通过多个工序对原材料进行加工,形成商品;

串行流 stream
并行流 parallelStream
Stream.of 静态方法
Arrays.stream静态方法
Stream.Iterate静态方法
Stream.generate静态方法
fileter过滤 / limit取前n / skip跳过前n
map映射 /concat合并 / sorted排序
distinct去重 / flatMap将流展开
foreach 逐一处理
count 统计个数
max&min 最大/小值
collect 数据集合
find 查找
match 匹配
reduce 聚合
Collection接口的实现类
可以使用
List
Set
....
创建Stream流
Map
arr数组
....
arr数组
无限流
迭代
生成
中间操作
终止操作

  2. Stream常用方法 (T表示泛型)

id方法名方法作用请求参数返回值类型操作类型
1创建流的六种方式创建
1.1stream所有 Collection 接口下的实现类(list,set,vector…)都可以通过此方法来获取stream串行流-Stream<E>创建
1.2parallelStream所有 Collection 接口下的实现类(list,set,vector…)都可以通过此方法来获取stream并行流-Stream<E>创建
1.3Arrays.stream静态方法,根据数组元素创建对应Stream流T[ ]Stream<T>创建
1.4Stream.of静态方法,根据任意元素创建对应Stream流
T…:请求参数属于可变长参数,详情查看 => 博客
TStream<T>创建
1.5Stream.iterate静态方法,创建无限流(迭代)
seed: 初始值
f : function->对初始值的迭代函数
T seed,
UnaryOperator<T> f
Stream<T>创建
1.6Stream.generate静态方法,创建无限流(生成)
Supplier属于系统内置函数式接口 -> 供给型接口
Supplier<T>Stream<T>创建
2filter过滤,符合条件的才会继续保留在Stream流中Predicate<T>Stream中间
3limit取用前几个longStream<T>中间
4skip跳过前几个longStream<T>中间
5map转换
将Stream流中的元素转为任意类型
Stream<T> -> Stream<R>
Function<T,R>Stream<R>中间
6concat静态方法,合并2个Stream流成为一个Stream流Stream<T>,
Stream<T>
Stream<T>中间
7sorted排序-Stream<T>中间
8distinct去重
distinct依赖Object的equals方法来判断是否是相同对象,如果需按照某元素去重时,注意重新定义equals方法
-Stream<T>中间
9flatMap将流展开Function<T,R>Stream<R>中间
10forEach逐一处理Consumer<T>void终止
11count统计个数-long终止操作
12collect参数 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终止
13max和min最大(小)值
Comparator<T> :比较器,指定比较大小的方式
Optional<T> :返回泛型T,包装一层Optional判空,这样就可以允许返回空值
Comparator<T>Optional<T>终止
14find查找
1. findFirst:如果一个集合数据是有序的,而且你要查找符合条件的第一条数据。这时用findFirst是比较合适的。
2. findAny:返回任意一条数据,不关心顺序。该方法不能保证获取的一定是流中的第一条数据。且在并行流中,findAny限制更少。
-Optional<T>终止
15match匹配
1. allMatch 全部匹配,则返回true
2. anymatch 任一一条匹配,则返回true
3. nonematch 全部不匹配,则返回true
Predicate<T>boolean终止
16reduce(accumulator)聚合
聚合的含义就是将多个值经过特定计算之后得到单个值
参数1. accumulator 计算方式
BinaryOperator<T>accumulatorOptional<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:实现数据的处理
longOptional<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函数式接口参数类型返回类型包含方法
⭐️1Predicate<T>
断言型接口
Tbooleanboolean test(T t)
⭐️2Consumer<T>
消费型接口
Tvoid void accept(T t)
⭐️3Function<T,R>
函数型接口
TRR apply(T t)
⭐️4Supplier<T>
供给型接口
TT get()

一、Predicate扩展接口

id函数式接口参数类型返回类型包含方法
1.1BiPredicate<T,U>T,Ubooleanboolean test(T t, U u)
1.2IntPredicateintbooleanboolean test(int value)
1.3LongPredicatelongbooleanboolean test(long value)
1.4DoublePredicatedoublebooleanboolean test(double value)

二、Consumer扩展接口

id函数式接口参数类型返回类型包含方法
2.1BiConsumer<T,U,R>T,Uvoidvoid accept(T t, U u)
2.2IntConsumerintvoid void accept(int value)
2.3LongConsumerlongvoid void accept(long value)
2.4DoubleConsumerdoublevoid void accept(double value)
三、Function扩展接口
id函数式接口参数
类型
返回
类型
包含方法
:–:–:–:–:–
3.1BiFunction<T,U,R>T,URR apply(T t, U u)
3.2IntFunction<R>intRR apply(int value)
3.3LongFunction<R>longRR apply(long value)
3.4DoubleFunction<R>doubleRR apply(double value)
3.5IntToLongFunctionintlonglong applyAsLong(int value)
3.6IntToDoubleFunctionintdoubledouble applyAsDouble(int value)
3.7LongToIntFunctionlongintint applyAsInt(long value)
3.8LongToDoubleFunctionlongdoubledouble applyAsDouble(long value)
3.9DoubleToIntFunctiondoubleintint applyAsInt(double value)
3.10DoubleToLongFunctiondoublelonglong applyAsLong(double value)
3.11BinaryOperator<T>
extens
BiFunction<T,U,R>
T,TTminBy
在这里插入图片描述
maxBy
在这里插入图片描述
3-.12UnaryOperator<T>
extends
Function<T, T>
TTstatic 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值