19 -- 20. Java学习 -- JDK1.8新特性

19 – 20. Java学习 – JDK1.8新特性

1. lambda表达式

lambda表达式,是jdk1.8的新特性,其实就是匿名函数的另一种写法,目的是用来简化代码。lambda表达式的使用前提是必须存在函数式接口。

所谓的函数式接口,就是一个接口里面只能有一个抽象方法。java官方也定义了很多内置的函数式接口,这些函数式接口使用 @FunctionalInterface 注解修饰。

自己定义函数式接口时, @FunctionalInterface 注解可以写,也可以不写。标注 @FunctionalInterface 注解会自动约束仅让在接口中声明一个抽象方法,多的会报错。

1.1. 不带参数和返回值

lambda表达式语法格式:

() -> {
	函数体...
}

例子:lambda表达式的基本使用

// 定义函数式接口
@FunctionalInterface
interface PersonDao {
    public void show();
}
// 测试
@Test
public void test01() {
    PersonDao per = () -> {
        System.out.println("使用lambda表达式实现");
    };
    per.show();
}

1.2. 带参数和返回值

带参数的lambda表达式中,( )中的参数类型可以不写,lambda表达式会自动匹配。

lambda表达式语法格式:

(参数列表) ->{
	// 函数体
	return 结果;
}

例子

// 定义函数式接口
@FunctionalInterface
interface NumDao {
    public int show(int num);
}
// 测试
@Test
public void test02() {
    NumDao num = (num1) -> {
        return num1 * 10;
    };
    System.out.println(num.show(10));   // 100
}

以上代码还可以继续简化。

参数只有一个函数体代码有且仅有一行,那么前面的 ( ) 和后面的 { } 以及 return关键字 都可以被省略。

// 定义函数式接口
@FunctionalInterface
interface NumDao {
    public int show(int num);
}
// 测试
@Test
public void test03() {
    NumDao num = num1 -> num1 * 10;
    System.out.println(num.show(20));
}

当有多个参数时,有几个参数括号中就写几个参数。

// 定义函数式接口
interface AvgDao {
    public double show(int num1, int num2);
}
// 测试
@Test
public void test04() {
    AvgDao avg = (num1, num2) -> (num1 + num2) / 2;
    System.out.println(avg.show(10, 20));   // 15.0
}

Lambda表达式的使用总结:

  • lambdabaioda前提:必须存在函数式接口,只能对函数式接口中的方法进行实现。要求接口中有且只有一个抽象方法
  • 在定义lambda表达式的时候,定义参数的时候,参数列表中的参数类型可以省略,因为编译器可以自行推断参数的数据类型
  • 如果参数列表中的参数有且只有一个,那么 圆括号( ) 是可以省略的
  • 如果lambda表达式的函数体中的代码有且只有一行,那么 花括号{ } 也是可以省略的
  • 如果lambda表达式的函数体中的代码有且只有一行代码,代码是使用return关键字返回数据,此时return关键字也可以省略

2. 函数式接口

函数式接口就是只能定义一个抽象方法的接口。一般使用注解@FunctionalInterface注解修饰。

在java中,官方提供了很多内置的函数式接口。比如Runnable接口,Comparator接口,都属于函数式接口。

2.1. 内置函数式接口的介绍

在java的java.util.function包下面,定义了很多内置的函数式接口

  1. 消费型函数式接口

特点:接收传递的参数,但是不返回任何数据

// 消费型函数式接口
@FunctionalInterface
public interface Consumer<T> { 
    /**
     * 接收传递的参数,但是不返回任何数据
     */
    void accept(T t);
}
  1. 供给型函数式接口

特点:不传递任何参数,方法的返回值类型就是泛型接口中定义的数据类型

// 供给型函数式接口
@FunctionalInterface
public interface Supplier<T> { 
    /**
     * 不传递任何参数,方法的返回值类型就是泛型接口中定义的数据类型
     */
	T get();
}
  1. 函数型函数式接口

特点:传递的参数为T,返回值为R

// 函数型函数式接口
@FunctionalInterface
public interface Function<T, R> { 
    /**
     * 传递的参数为T,返回值为R
     */
	R apply(T t);
 }
  1. 断言型函数式接口

特点:判断传递的参数T是否满足指定的约束或者规范,满足返回true,不满足返回false

// 断言型函数式接口
@FunctionalInterface
public interface Predicate<T> {  
    /**
     * 判断传递的参数T是否满足指定的约束或者规范,满足返回true,不满足返回false。
     */
    boolean test(T t);
 }

2.2. 消费型函数式接口的使用

特点:接收传递的参数,但是不返回任何数据

public class ConsumerFunctionDemo {
    public void getResult(double money, Consumer<Double> consumer) {
        consumer.accept(money);
    }

    // 原来的写法
    @Test
    public void beforeTest() {
        getResult(22.34, new Consumer<Double>() {
            @Override
            public void accept(Double aDouble) {
                System.out.println("金额是: " + aDouble);    // 金额是: 22.34
            }
        });
    }

    // 基于lambda表达式的写法
    @Test
    public void newTest() {
        getResult(8.88, money -> System.out.println("金额是: " + money));  // 金额是: 8.88
    }
}

2.3. 供给型函数式接口

特点:不传递任何参数,方法的返回值类型就是泛型接口中定义的数据类型

public class SupplierFunctionDemo {
    public String generateValidateCode(Supplier<String> supplier) {
        return supplier.get();
    }

    // 原来的写法
    @Test
    public void beforeTest() {
        String s = generateValidateCode(new Supplier<String>() {
            @Override
            public String get() {
                return RandomUtil.randomNumbers(6);
            }
        });
        System.out.println("验证码是: " + s);   // 验证码是: 805808
    }

    // lambda表达式的写法
    @Test
    public void newTest() {
        String s = generateValidateCode(() -> RandomUtil.randomNumbers(6));
        System.out.println("验证码是: " + s);   // 验证码是: 196504
    }
}

以上例子中,用到了一个很有用的工具库 – hutool-all,想使用这个库需要在Maven中引入依赖:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.4.0</version>
</dependency>

hutool-all库包含了许多功能模块:

  • 字符串处理: 提供了丰富的字符串处理方法,包括字符串切割、连接、格式化、Unicode转换等

  • 日期时间处理: 提供了日期时间的格式化、解析、计算、时区转换等功能

  • 加密解密: 支持常见的加密算法,如MD5、SHA等,也包括AES、RSA等对称和非对称加密解密方法

  • 文件操作: 提供了文件读写、复制、移动、文件类型判断等操作的工具方法

  • 网络通信: 提供了HTTP客户端、服务器等网络通信相关的工具类,简化了HTTP请求的发送和处理

  • 图片处理: 包含了图片缩放、水印添加、图片格式转换等图片处理功能

比如以上例子中就使用了hutool-all库来生成随机验证码。

2.4. 函数型函数式接口

特点:传递的参数为T,返回值为R

public class FunctionDemo {
    public Integer getLength(String message, Function<String, Integer> function) {
        return function.apply(message);
    }

    // 原来的写法
    @Test
    public void beforeTest() {
        Integer length = getLength("HelloWorld", new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return s.length();
            }
        });
        System.out.println("字符串长度为: " + length);    // 字符串长度为: 10
    }

    // lambda表达式写法
    @Test
    public void newTest() {
        Integer length = getLength("Bye", s -> s.length());
        System.out.println("字符串长度为: " + length);    // 字符串长度为: 3
    }
}

函数式接口,也可以作为方法的返回值:

// 函数式接口做返回值
// 原来的写法
public Function<Integer, String> getMethod() {
    return new Function<Integer, String>() {
        @Override
        public String apply(Integer integer) {
            return "hello" + integer;
        }
    };
}

// lambda表达式写法
public Function<Integer, String> newGetMethod() {
    return integer -> "hello" + integer;
}

2.5. 断言型函数式接口

特点:判断传递的参数T是否满足指定的约束或者规范,满足返回true,不满足返回false

public class PredicateFunctionDemo {
    public List<String> filterList(List<String> list, Predicate<String> pre) {
        ArrayList<String> newList = new ArrayList<>();
        for (String str : list) {
            if (pre.test(str)) {
                newList.add(str);
            }
        }
        return newList;
    }

    public static List<String> list = new ArrayList<>();

    static {
        list.add("北京");
        list.add("上海");
        list.add("南京");
        list.add("深圳");
        list.add("广州");
        list.add("广东");
    }

    // 原来的写法
    @Test
    public void beforeTest() {
        List<String> newList = filterList(list, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("京");
            }
        });

        System.out.println(newList);    // [北京, 南京]
    }

    // lambda表达式写法
    @Test
    public void newTest() {
        List<String> newList = filterList(list, s -> s.contains("广"));
        System.out.println(newList);    // [广州, 广东]
    }
}

3. 方法引用

方法引用是一种简化lambda表达式的方式,也是对lambda表达式的深层次运用。其使编写代码时可以直接引用类的方法,而不需要手写完整的lambda表达式。

**“::”**是方法引用的操作符,方法引用可以使代码更简洁(清晰易读这个优点我觉得有待商榷)。

方法引用需要注意

  1. 需要函数式接口
  2. 被引用的方法必须已经存在
  3. 被引用方法的形参和返回值需要跟抽象方法保持一致
  4. 被引用方法的功能需要满足当前的需求

方法引用的4种语法形式

  1. 静态方法引用:类名 :: 静态方法名称
  2. 实例方法引用:对象名 :: 实例方法名称
  3. 构造方法引用:类名 :: new
  4. 特定方法引用:类名 :: 方法名

3.1. 体验方法引用

例1

public class MethodReferenceDemo1 {
    // 原来的写法
    @Test
    public void test01() {
        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("hello");
    }

    // lambda表达式写法
    @Test
    public void test02() {
        Consumer<String> con = s -> System.out.println(s);
        con.accept("world");
    }

    // 方法引用
    @Test
    public void test03() {
        Consumer<String> con = System.out::println;
        con.accept("java");
    }
}

例2

@Data
@NoArgsConstructor
@AllArgsConstructor
class Employee {

    private Integer id;
    private String name;
    private Integer age;
    private Integer salary;
}

public class MethodReferenceDemo2 {
    // 原来的写法
    @Test
    public void test01() {
        Employee aaa = new Employee(101, "AAA", 22, 1000);
        Supplier<String> supplier = new Supplier<String>() {
            @Override
            public String get() {
                return aaa.getName();
            }
        };
        System.out.println(supplier.get());
    }

    // lambda表达式写法
    @Test
    public void test02() {
        Employee bbb = new Employee(102, "BBB", 23, 1500);
        Supplier<String> supplier = () -> bbb.getName();
        System.out.println(supplier.get());
    }

    // 方法引用
    @Test
    public void test03() {
        Employee ccc = new Employee(102, "CCC", 21, 2000);
        Supplier<String> supplier = ccc::getName;
        System.out.println(supplier.get());
    }
}

3.2. 静态方法引用(类名 :: 静态方法名称)

条件:lambda表达式里面有且只调用了1个静态方法,并且前后参数一致

例1

public class StaticMethodReferenceDemo1 {
    // 原来的写法
    @Test
    public void test01() {
        Comparator<Integer> comparator = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2); // 静态方法,比较两个int型数字的值
            }
        };
        System.out.println(comparator.compare(12, 23));
    }

    // lambda表达式写法
    @Test
    public void test02() {
        Comparator<Integer> comparator = (o1, o2) -> Integer.compare(o1, o2);
        System.out.println(comparator.compare(23, 13));
    }

    // 方法引用
    @Test
    public void test03() {
        Comparator<Integer> comparator = Integer::compare;
        System.out.println(comparator.compare(45, 23));
    }
}

例2

public class StaticMethodReferenceDemo2 {
    // 原来的写法
    @Test
    public void test01() {
        Function<Double, Long> function = new Function<Double, Long>() {
            @Override
            public Long apply(Double aDouble) {
                return Math.round(aDouble); // 将一个数字四舍五入为最接近的整数
            }
        };
        System.out.println(function.apply(3.1459265358));
    }

    // lambda表达式写法
    @Test
    public void test02() {
        Function<Double, Long> function = d -> Math.round(d);
        System.out.println(function.apply(2.23333));
    }

    // 方法引用
    @Test
    public void test03() {
        Function<Double, Long> function = Math::round;
        System.out.println(function.apply(345.21432));
    }
}

实践:将Employee数组按薪资排序

public class StaticMethodReferenceDemo3 {
    public static Employee[] emp = {new Employee(101, "AAA", 23, 2544),
            new Employee(102, "BBB", 21, 2654),
            new Employee(103, "CCC", 22, 2457),
            new Employee(104, "DDD", 25, 7542),
            new Employee(105, "EEE", 24, 2000)};

    // 原来的写法
    @Test
    public void test01() {
        Arrays.sort(emp, new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return o1.getSalary() - o2.getSalary();
            }
        });
        for (Employee e : emp) {
            System.out.println(e);
        }
    }

    // lambda表达式写法
    @Test
    public void test02() {
        Arrays.sort(emp, (o1, o2) -> o1.getSalary() - o2.getSalary());
        for (Employee e : emp) {
            System.out.println(e);
        }
    }

    // 方法引用
    @Test
    public void test03() {
        Arrays.sort(emp, EmployeeMethod::compareEmployee);
        for (Employee e : emp) {
            System.out.println(e);
        }
    }
}

class EmployeeMethod {
    public static int compareEmployee(Employee e1, Employee e2) {
        return e1.getSalary() - e2.getSalary();
    }
}

3.3. 实例方法引用(对象名 :: 实例方法名称)

条件:lambda表达式里面前后参数是一致,并且方法体里面的方法是通过对象调用

例1

public class MethodReferenceDemo3 {
    public interface UserDao {
        public String save(String str1, String str2);
    }

    // 原来的写法
    @Test
    public void test01() {
        UserMethod userMethod = new UserMethod();
        UserDao userDao = new UserDao() {
            @Override
            public String save(String str1, String str2) {
                return userMethod.generateStr(str1, str2);
            }
        };
        System.out.println(userDao.save("hello, ", "world"));
    }

    // lambda表达式写法
    @Test
    public void test02() {
        UserMethod userMethod = new UserMethod();
        UserDao userDao = (s1, s2) -> userMethod.generateStr(s1, s2);
        System.out.println(userDao.save("Wow, ", "interesting"));
    }

    // 方法引用
    @Test
    public void test03() {
        UserMethod userMethod = new UserMethod();
        UserDao userDao = userMethod::generateStr;
        System.out.println(userDao.save("No, ", "not interesting"));
    }
}

class UserMethod {
    public String generateStr(String str1, String str2) {
        return str1.concat(str2);
    }
}

实践:遍历集合中的元素

public class MethodReferenceDemo3 {

    public static ArrayList<Employee> emp = new ArrayList<>();

    static {
        emp.add(new Employee(101, "AAA", 23, 4516));
        emp.add(new Employee(102, "BBB", 24, 4599));
        emp.add(new Employee(103, "CCC", 21, 6542));
        emp.add(new Employee(104, "DDD", 25, 3485));
        emp.add(new Employee(105, "EEE", 23, 7846));
        emp.add(new Employee(106, "FFF", 24, 2514));
    }

    // 原来的写法
    @Test
    public void test04() {
        emp.forEach(new Consumer<Employee>() {
            @Override
            public void accept(Employee employee) {
                System.out.println(employee);
            }
        });
    }

    // lambda表达式写法
    @Test
    public void test05() {
        emp.forEach(e -> System.out.println(e));
    }

    // 方法引用
    @Test
    public void test06() {
        emp.forEach(System.out::println);
    }
}

3.4. 构造方法引用(类名 :: new)

条件:lambda表达式里面只创建对象,并且前后参数一致

例1

public class ConstructorReferenceDemo1 {
    // 原来的写法
    @Test
    public void test01() {
        Supplier<Employee> aaa = new Supplier<Employee>() {
            @Override
            public Employee get() {
                return new Employee();
            }
        };
        Employee employee = aaa.get();
        System.out.println(employee);
    }

    // lambda表达式写法
    @Test
    public void test02() {
        Supplier<Employee> supplier = () -> new Employee();
        Employee employee = supplier.get();
        System.out.println(employee);
    }

    // 方法引用
    @Test
    public void test03() {
        Supplier<Employee> supplier = Employee::new;
        Employee employee = supplier.get();
        System.out.println(employee);
    }
}

例2

public class ConstructorReferenceDemo2 {
    // 原来的写法
    @Test
    public void test01() {
        BiFunction<String, Integer, Employee> biFunction = new BiFunction<String, Integer, Employee>() {
            @Override
            public Employee apply(String s, Integer integer) {
                return new Employee(s, integer);
            }
        };
    }

    // lambda表达式写法
    @Test
    public void test02() {
        BiFunction<String, Integer, Employee> biFunction = (s, i) -> new Employee(s, i);
    }

    // 方法引用
    @Test
    public void test03() {
        BiFunction<String, Integer, Employee> biFunction = Employee::new;
    }
}

3.5. 特定方法引用(类名 :: 方法名)

条件:lambda表达式里面只有一个实例方法,并且前面参数列表中的第一个参数是主调参数(通过这个参数调用方法),后面的所有参数被当成入参传入到方法里

例1

public class MethodReferenceDemo4 {
    public static String[] str = {"hello", "basketball", "football",
            "swimming", "flying", "Fight",
            "Zoom", "Hello"};

    // 原来的写法
    @Test
    public void test01() {
        // 大写小写分开排序
        Arrays.sort(str);
        System.out.println(Arrays.toString(str));
        // 大小写排序不分开
        Arrays.sort(str, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareToIgnoreCase(o2);
            }
        });
        System.out.println(Arrays.toString(str));
    }

    // lambda表达式写法
    @Test
    public void test02() {
        Arrays.sort(str, (o1, o2) -> o1.compareToIgnoreCase(o2));
        System.out.println(Arrays.toString(str));
    }

    // 方法引用
    @Test
    public void test03() {
        Arrays.sort(str, String::compareToIgnoreCase);
        System.out.println(Arrays.toString(str));
    }

}

案例:比较两个字符串的内容是否一致,忽略大小写比较

public class MethodReferenceDemo4 {
    public static String[] str = {"hello", "basketball", "football",
            "swimming", "flying", "Fight",
            "Zoom", "Hello"};


    // 原来的写法
    @Test
    public void test04() {
        StringDao stringDao = new StringDao() {
            @Override
            public boolean show(String s1, String s2) {
                return s1.equalsIgnoreCase(s2);
            }
        };
        System.out.println(stringDao.show("Hello", "hello"));
    }

    // lambda表达式写法
    @Test
    public void test05() {
        StringDao stringDao = (s1, s2) -> s1.equalsIgnoreCase(s2);
        System.out.println(stringDao.show("world", "World"));
    }

    // 方法引用
    @Test
    public void test06() {
        StringDao stringDao = String::equalsIgnoreCase;
        System.out.println(stringDao.show("WOW", "wow"));
    }
}

4. Stream流

Stream流也是JDK1.8的性特性,其提供了非常强大的API帮助我们来进行集合或数组操作。Stream流简化了我们对数组或集合中元素的操作,使我们更专注于做什么(What),而不是怎么做(How)。

如何使用Sream流

  • 创建Stream流。通过集合或数组来创建
  • 进行中间操作。一个中间操作由很多小的操作组成,形成一个操作链,这个操作链可以对集合或数组中的元素进行处理。比如,集合或数组元素的去重、过滤等
  • 进行终止操作。一旦执行终止操作,就会将中间操作的所有操作链全部执行,并产生最终的结果

4.1. 体验Stream流

例子:在集合中存储一些名字,找出名字中姓王的名字,并且名字的长度是3

public class StreamDemo1 {
    public static ArrayList<String> list = new ArrayList<>();

    static {
        list.add("王上");
        list.add("赵佐佑");
        list.add("王商夏");
        list.add("刘下");
        list.add("王流漆");
        list.add("李斯乌");
    }

    public List<String> getNewNames(List<String> list, Predicate<String> predicate) {
        List<String> newList = new ArrayList<>();
        for (String s : list) {
            if (predicate.test(s)) {
                newList.add(s);
            }
        }
        return newList;
    }

    // 原来的方法
    @Test
    public void test01() {
        List<String> newList = getNewNames(list, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.startsWith("王") && s.length() == 3;
            }
        });
        System.out.println(newList);
    }

    // Stream流
    @Test
    public void test02() {
        /*List<String> newList = list.stream().filter(s -> s.startsWith("王") && s.length() == 3).
                collect(Collectors.toList());*/
        List<String> newList = list.stream().filter(s -> s.startsWith("王")).
                filter(s -> s.length() == 3).collect(Collectors.toList());
        System.out.println(newList);
    }
}

4.2. 创建Stream流

创建Stream流对象的方法:

  • 集合对象.stream():基于集合对象创建Stream流对象
  • Arrays.stream(数组对象):根据数组对象创建Stream流对象
  • Stream.of(T … values):向Stream中添加多个数据

备注of方法的参数其实是一个可变参数,所以也支持数组

4.3. 常用方法

方法分为两种:终结方法、非终结方法

终结方法:返回值类型不再是Stream接口自身类型的方法,因此不再支持类似StringBuilder那样的链式调用。本小节中,终结方法包括countforEach方法

非终结方法:返回值类型仍然是Stream接口自身类型的方法,因此支持链式调用。除了终结方法外,其余方法均为非终结方法

4.3.1. 非终结方法

筛选与切片

  • filter:指定条件对集合中的元素进行过滤
  • limit:取出前几个元素
  • skip:跳过指定个数的元素
  • distinct:对集合中的元素去重
public class StreamDemo2 {
    public static List<Employee> list = new ArrayList<>();

    static {
        list.add(new Employee(101, "AAA", 23, 5489));
        list.add(new Employee(102, "BBB", 21, 4587));
        list.add(new Employee(103, "CCC", 22, 2513));
        list.add(new Employee(104, "DDD", 26, 6489));
        list.add(new Employee(105, "EEE", 25, 7000));
        list.add(new Employee(106, "FFF", 24, 4050));
    }

    @Test
    public void test01() {
        // 创建Stream流对象
        Stream<Employee> stream = list.stream();
        // 找到薪资大于5000的人
        stream.filter(s -> s.getSalary() > 5000).forEach(System.out::println);
        System.out.println("======================================");
        // 输出所有元素
        stream.forEach(System.out::println);
        System.out.println("======================================");
        //输出前三个元素
        stream.limit(3).forEach(System.out::println);
        System.out.println("======================================");
        // 跳过指定元素个数
        stream.skip(4).forEach(System.out::println);
        System.out.println("======================================");
        // 添加元素去重
        list.add(new Employee(105, "EEE", 25, 7000));
        list.add(new Employee(106, "FFF", 24, 4050));
        list.add(new Employee(105, "EEE", 25, 7000));
        list.add(new Employee(106, "FFF", 24, 4050));
        // 输出元素
        stream.forEach(System.out::println);
        System.out.println("======================================");
        stream.distinct().forEach(System.out::println);
    }
}

映射

  • map:映射方法
public class StreamDemo2 {
    public static List<Employee> list = new ArrayList<>();

    static {
        list.add(new Employee(101, "AAA", 23, 5489));
        list.add(new Employee(102, "BBB", 21, 4587));
        list.add(new Employee(103, "CCC", 22, 2513));
        list.add(new Employee(104, "DDD", 26, 6489));
        list.add(new Employee(105, "EEE", 25, 7000));
        list.add(new Employee(106, "FFF", 24, 4050));
    }

    @Test
    public void test02(){
        List<String> stringList= Arrays.asList("aa","bb","cc","dd","ee");
        // map映射转大写
        stringList.stream().map(s->s.toUpperCase()).forEach(System.out::println);
        // 长度超过5的名字输出
        list.add(new Employee(106, "FFFFFF", 24, 4050));
        list.stream().map(s->s.getName()).filter(s->s.length() > 5).forEach(System.out::println);
    }
}

排序

  • sorted:给自然元素进行排序
  • sorted:制定比较规则,再进行排序
public class StreamDemo2 {
    public static List<Employee> list = new ArrayList<>();

    static {
        list.add(new Employee(101, "AAA", 23, 5489));
        list.add(new Employee(102, "BBB", 21, 4587));
        list.add(new Employee(103, "CCC", 22, 2513));
        list.add(new Employee(104, "DDD", 26, 6489));
        list.add(new Employee(105, "EEE", 24, 7000));
        list.add(new Employee(106, "FFF", 24, 4050));
    }

    @Test
    public void test03() {
        List<Integer> integers = Arrays.asList(99, 12, 24, 11, 45, 56, 78, 42, 66);
        // 对自然元素进行排序
        integers.stream().sorted().forEach(System.out::println);
        System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++");
        // 对员工年龄进行排序
        list.stream().sorted((e1, e2) -> e1.getAge() - e2.getAge()).forEach(System.out::println);
        System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++");
        // 先对员工年龄进行排序,如果年龄一样,再按照薪资进行排序
        list.stream().sorted((e1, e2) -> {
            // 先比较年龄
            int compare = Integer.compare(e1.getAge(), e2.getAge());
            if (compare != 0) {
                return compare;
            } else {
                return Integer.compare(e1.getSalary(), e2.getSalary());
            }
        }).forEach(System.out::println);
    }
}
4.3.2. 终结方法

终结方法

  • forEach:逐一处理
  • count:统计个数
public class StreamDemo3 {
    public static List<Employee> list = new ArrayList<>();

    static {
        list.add(new Employee(101, "AAA", 23, 5489));
        list.add(new Employee(102, "BBB", 21, 4587));
        list.add(new Employee(103, "CCC", 22, 2513));
        list.add(new Employee(104, "DDD", 26, 6489));
        list.add(new Employee(105, "EEE", 24, 7000));
        list.add(new Employee(106, "FFF", 24, 4050));
    }

    @Test
    public void test01() {
        // 查询所有员工总数
        long count = list.stream().count();
        System.out.println("总数是: " + count);
        // 查询薪资在4000元的员工个数
        long count_salary = list.stream().filter(e -> e.getSalary() >= 4000).count();
        System.out.println(count_salary);
        // 找出集合中薪资最高的薪水
        Integer max_salary = list.stream().map(i -> i.getSalary()).max((o1, o2) -> o1 - o2).get();
        System.out.println(max_salary);
    }
}

Stream流中的其他操作

@Test
public void test03(){
    List<Employee> empList = EmployeeData.employeeList;
    // collect 将过滤之后的元素放在新的集合中
    List<Employee> emps = empList.stream().filter(e -> e.getSalary() > 5000).collect(Collectors.toList());
    // 循环遍历集合中的元素
    emps.forEach(System.out::println);
}

5 Optional类

Optional类主要是避免在开发中对象出现空指针异常

5.1. Optional对象创建

  • Optional.of(T t):根据指定对象构建一个Optional类型的对象,of里面的对象一定不能为空,否则出现空指针异常

  • Optional.ofnullable(T t):根据指定对象构建一个Optional类型的对象,ofNullable方法里面可以传递空对象,也可以传递非空对象

public class OptionalDemo1 {
    @Test
    public void test01() {
        Girl girl = new Girl();
        // 必须传入一个非空对象,否则报错
        Optional<Girl> optional1 = Optional.of(girl);
        System.out.println(optional1);
//        Optional<Girl> optional2 = Optional.of(null);     报错
//        System.out.println(optional2);
    }
    
    @Test
    public void test02(){
        Girl girl = new Girl();
        Optional<Girl> optional1 = Optional.ofNullable(girl);
        System.out.println(optional1);
        Optional<Object> optional2 = Optional.ofNullable(null);
        System.out.println(optional2);
    }
}

class Girl {

}

5.2. Optional类的基本使用

public class OptionalDemo2 {
    // 使用 Optional 类来优化方法
    public String getGirlName(Boy boy) {
        Optional<Boy> boyOptional = Optional.ofNullable(boy);
        // 通过orElse方法来避免空指针异常出现的风险,如果boy对象为null,则使用orElse里面的默认对象
        Boy boy1 = boyOptional.orElse(new Boy(null));
        Girl girl = boy1.getGirl();
        // 如果girl对象为null时,也用orElse方法来判空,并设置默认值
        Optional<Girl> girlOptional = Optional.ofNullable(girl);
        Girl girl1 = girlOptional.orElse(new Girl("AAA"));
        return girl1.getName();
    }

    @Test
    public void test01() {
        Boy boy = null;
        String girlName = getGirlName(boy);
        System.out.println(girlName);

        Boy boy1 = new Boy(new Girl("BBB"));
        System.out.println(getGirlName(boy1));
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Boy {
    Girl girl;
}
  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值