Loading

Java SE 8 新增特性

Java SE 8 新增特性

作者:Grey

原文地址:

博客园:Java SE 8 新增特性

CSDN:Java SE 8 新增特性

源码

源仓库: Github:java_new_features

Lambda 表达式

Java SE 8 里面最大的更新莫过于支持 Lambda 表达式,Oracle 官网给了一个很好的示例说明,见:Lambda Expressions,以下示例参考了这个官方说明文档。

假设我们定义一个 Person 类,属性和方法如下

public class Person {
    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;
    // 省略get/set方法

    public void printPerson() {
        // 打印Person信息

    }
    public static List<Person> createRoster() {
        // 获取Person列表
    }

    public enum Sex {
        MALE, FEMALE
    }
}

如果要获取某个年龄段的所有 Person 信息,我们可能会写出如下代码

// 查询大于age的所有人员信息
public static void printPersonsOlderThan(List<Person> roster, int age) {
    for (Person p : roster) {
        if (p.getAge() >= age) {
            p.printPerson();
        }
    }
}
// 查询年龄在[low, high)区间内的所有人员信息
public static void printPersonsWithinAgeRange(
    List<Person> roster, int low, int high) {
    for (Person p : roster) {
        if (low <= p.getAge() && p.getAge() < high) {
            p.printPerson();
        }
    }
}

这样写的缺点是扩展性不好,如果有新的规则,我们需要增加多个同样类型的方法。

更好的一种方式是,定义一个 Local Class,将规则分离出来,这样一来,规则无论如何变化,主流程的代码是不需要调整的,以上述例子来说明。我们定义如下接口

interface CheckPerson {
    boolean test(Person p);
}

这个接口用于抽象出规则的定义,在主流程中,我们把这个接口设计到参数中,代码如下:

public static void printPersons(List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

这样我们就做到了规则和主流程分离,比如我们要定义一个规则,只需要实现 CheckPerson 接口

// 年龄在[18,25]区间内的男性人员信息
class CheckPersonEligibleForSelectiveService implements CheckPerson {
    public boolean test(Person p) {
        return p.gender == Person.Sex.MALE &&
            p.getAge() >= 18 &&
            p.getAge() <= 25;
    }
}

在调用的时候,只需要把这个规则作为参数传入即可

printPersons(roster, new CheckPersonEligibleForSelectiveService());

上述示例还可以转换成匿名类的写法,如下:

printPersons(roster,new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

也可以转换成 Java SE 8 中新增的 Lambda 表达式的写法,如下:

printPersons(roster,
    (Person p) -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25
);

由于 CheckPerson 这个接口只有一个方法,所以这又是一个函数式接口,在我们这个例子的场景下,我们可以用 Predicate<T> 来替换 CheckPerson 接口。因为 Predicate<T> 接口定义就是

interface Predicate<T> {
    boolean test(T t);
}

使用 Predicate<T> 以后,代码如下:

public static void printPersonsWithPredicate(List<Person> roster, Predicate<Person> tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

最后,代码又可以简化成如下形式:

printPersonsWithPredicate(roster,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25
);

printPersonsWithPredicate 方法重构成如下形式:

public static void printPersonsWithPredicate(
    List<Person> roster, Predicate<Person> tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

还可以做进一步的优化,我们可以指定一个不同的动作来执行那些满足条件的 Person 实例,而不是直接调用 printPerson 方法。可以用一个 Lambda 表达式来指定这个动作。这里引入了 Consumer 接口。

public static void processPersons(
    List<Person> roster,
    Predicate<Person> tester,
    Consumer<Person> block) {
        for (Person p : roster) {
            if (tester.test(p)) {
                block.accept(p);
            }
        }
}

这样,我们就把打印行为也给分离出来了,主流程调用的代码可以进一步简化成

processPersons(
     roster,
     p -> p.getGender() == Person.Sex.MALE
         && p.getAge() >= 18
         && p.getAge() <= 25,
     p -> p.printPerson()
);

经过上述重构和优化,我们可以定义不同条件下的不同行为

例如:

public static void processPersonsWithFunction(
    List<Person> roster,
    Predicate<Person> tester,
    Function<Person, String> mapper,
    Consumer<String> block) {
    for (Person p : roster) {
        if (tester.test(p)) {
            String data = mapper.apply(p);
            block.accept(data);
        }
    }
}

再如:

processPersonsWithFunction(
    roster,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25,
    p -> p.getEmailAddress(),
    email -> System.out.println(email)
);

可以做进一步的泛化:

public static <X, Y> void processElements(
    Iterable<X> source,
    Predicate<X> tester,
    Function <X, Y> mapper,
    Consumer<Y> block) {
    for (X p : source) {
        if (tester.test(p)) {
            Y data = mapper.apply(p);
            block.accept(data);
        }
    }
}

针对的场景就是一个集合,经过某些过滤,取出满足条件的数据,然后把这个数据进行加工,例如:

processElements(
    roster,
    p -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25,
    p -> p.getEmailAddress(),
    email -> System.out.println(email)
);

最后,使用 Stream ,让整个代码变的简洁优雅

roster
    .stream()
    .filter(
        p -> p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25)
    .map(p -> p.getEmailAddress())
    .forEach(email -> System.out.println(email));

除了可以使用 Lambda 表达式创建匿名方法,我们还可以使用方法引用来替代 lambda 表达式,这样可读性会好一些,例如:

public class Person {
    // ...
    public static int compareByAge(Person a, Person b) {
        return a.birthday.compareTo(b.birthday);
    }

    // ...
}

按年龄排序,我们既可以这样写:

// 定义比较器
class PersonAgeComparator implements Comparator<Person> {
    public int compare(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
Arrays.sort(rosterAsArray, new PersonAgeComparator());

也可以使用 lambda 表达式

Arrays.sort(rosterAsArray,
    (Person a, Person b) -> {
        return a.getBirthday().compareTo(b.getBirthday());
    }
);

由于 Person 类中已经存在一个比较方法,我们可以通过方法引用来替代 Lambda 表达式

Arrays.sort(rosterAsArray,
    (a, b) -> Person.compareByAge(a, b)
);

这个 compareByAge 的参数列表和 Comparator<Person>.compare 方法的参数一致,可以简写成:

Arrays.sort(rosterAsArray, Person::compareByAge);

方法引用有如下几种类型

类型 语法 示例
静态方法 *ContainingClass*::*staticMethodName* Person::compareByAge MethodReferencesExamples::appendStrings
实例方法 *containingObject*::*instanceMethodName* myComparisonProvider::compareByName myApp::appendStrings2
对一个特定类型的任意对象的实例方法的引用 *ContainingType*::*methodName* String::compareToIgnoreCase String::concat
构造方法 *ClassName*::new HashSet::new

以下是示例

import java.util.function.BiFunction;

/**
 * @since 1.8
 */
public class MethodReferencesExamples {

    public static <T> T mergeThings(T a, T b, BiFunction<T, T, T> merger) {
        return merger.apply(a, b);
    }

    public static String appendStrings(String a, String b) {
        return a + b;
    }

    public String appendStrings2(String a, String b) {
        return a + b;
    }

    public static void main(String[] args) {

        MethodReferencesExamples myApp = new MethodReferencesExamples();

        // 使用 lambda 表达式
        System.out.println(MethodReferencesExamples.mergeThings("Hello ", "World!", (a, b) -> a + b));

        // 静态方法
        System.out.println(MethodReferencesExamples.mergeThings("Hello ", "World!", MethodReferencesExamples::appendStrings));

        // 实例方法
        System.out.println(MethodReferencesExamples.mergeThings("Hello ", "World!", myApp::appendStrings2));

        // 对一个特定类型的任意对象的实例方法的引用
        System.out.println(MethodReferencesExamples.mergeThings("Hello ", "World!", String::concat));
    }
}

接口支持默认方法和静态方法

直接看示例:

import java.time.*;

public interface TimeClient {
    void setTime(int hour, int minute, int second);
    void setDate(int day, int month, int year);
    void setDateAndTime(int day, int month, int year,
                               int hour, int minute, int second);
    LocalDateTime getLocalDateTime();
}
package defaultmethods;

import java.time.*;
import java.lang.*;
import java.util.*;

public class SimpleTimeClient implements TimeClient {

    private LocalDateTime dateAndTime;
    
    public SimpleTimeClient() {
        dateAndTime = LocalDateTime.now();
    }
    
    public void setTime(int hour, int minute, int second) {
        LocalDate currentDate = LocalDate.from(dateAndTime);
        LocalTime timeToSet = LocalTime.of(hour, minute, second);
        dateAndTime = LocalDateTime.of(currentDate, timeToSet);
    }
    
    public void setDate(int day, int month, int year) {
        LocalDate dateToSet = LocalDate.of(day, month, year);
        LocalTime currentTime = LocalTime.from(dateAndTime);
        dateAndTime = LocalDateTime.of(dateToSet, currentTime);
    }
    
    public void setDateAndTime(int day, int month, int year,
                               int hour, int minute, int second) {
        LocalDate dateToSet = LocalDate.of(day, month, year);
        LocalTime timeToSet = LocalTime.of(hour, minute, second); 
        dateAndTime = LocalDateTime.of(dateToSet, timeToSet);
    }
    
    public LocalDateTime getLocalDateTime() {
        return dateAndTime;
    }
    
    public String toString() {
        return dateAndTime.toString();
    }
    
    public static void main(String... args) {
        TimeClient myTimeClient = new SimpleTimeClient();
        System.out.println(myTimeClient.toString());
    }
}

上述代码中,如果要在接口中添加一个方法,那么所有实现这个接口的客户端都要重新实现这个方法,非常麻烦。

public interface TimeClient {
    void setTime(int hour, int minute, int second);
    void setDate(int day, int month, int year);
    void setDateAndTime(int day, int month, int year,
        int hour, int minute, int second);
    LocalDateTime getLocalDateTime();
    // 新增一个方法,所有的子类都要实现这个方法
    ZonedDateTime getZonedDateTime(String zoneString);
}

在 Java SE 8 中,接口可以支持默认方法,即:我们可以指定接口的某个方法的默认实现,这样的话,子类就不需要重写这个方法,可以使用接口的默认实现,同时,Java SE 8 中,接口也支持静态方法。

package defaultmethods;

import java.time.*;

public interface TimeClient {
    void setTime(int hour, int minute, int second);
    void setDate(int day, int month, int year);
    void setDateAndTime(int day, int month, int year,
                               int hour, int minute, int second);
    LocalDateTime getLocalDateTime();

    // 接口也支持静态方法实现
    static ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }
    // 默认实现,子类无须重写    
    default ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }
}

通过上述改造,所有子类都具备了接口默认方法的能力,子类可以直接调用接口的默认实现。

package defaultmethods;

import java.time.*;
import java.lang.*;
import java.util.*;

public class TestSimpleTimeClient {
    public static void main(String... args) {
        // NOTE:SimpleTimeClient无须做任何改动
        TimeClient myTimeClient = new SimpleTimeClient();
        System.out.println("Current time: " + myTimeClient.toString());
        // 调用默认实现
        System.out.println("Time in California: " +
            myTimeClient.getZonedDateTime("Blah blah").toString());
    }
}

当然,默认实现也可以修改,子类重写默认实现就可以了。

此外,接口支持静态方法实现,上述接口中的静态方法,可以直接使用

TimeClient.getZoneId("zoneID");

接口可以支持静态方法这一特性扩展了接口的功能,但是对于实现这个接口的子类没有影响,比如 Comparator 类中的 comparing 方法,示例

myDeck.sort(
    Comparator
        .comparing(Card::getRank)
        .thenComparing(Comparator.comparing(Card::getSuit)));

一些增强的 API

新增的包

java.util.function 
java.util.stream

调整的包

新增类 有调整的类
java.io UncheckedIOException BufferedReader
java.lang not applicable AutoCloseable ThreadLocal String Iterable CharSequence Boolean Integer Long Float Double
java.nio.file not applicable Files
java.util PrimitiveIterator Spliterator DoubleSummaryStatistics IntSummaryStatistics LongSummaryStatistics Optional OptionalDouble OptionalInt OptionalLong Spliterators SplittableRandom StringJoiner Arrays BitSet Collection Comparator Iterator List Map Map.Entry LinkedHashMap Random TreeMap
java.util.concurrent not applicable ThreadLocalRandom
java.util.jar not applicable JarFile
java.util.zip not applicable ZipFile
java.util.logging not applicable Logger
java.util.regex not applicable Pattern

具体可参考:New and Enhanced APIs That Take Advantage of Lambda Expressions and Streams in Java SE 8

List 转 Map

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

// list转map
public class List2Map {
    /**
     * key name, value number
     */
    static void sample1() {
        List<Car> list = new ArrayList<>();
        list.add(new Car("A", 1));
        list.add(new Car("B", 2));
        list.add(new Car("C", 3));

        // to map,key car name,value ,car number
        Map<String, Integer> carMap = list.stream().collect(Collectors.toMap(Car::getName, Car::getNum));

        System.out.println(carMap);
    }

    /**
     * key name value object
     */
    static void sample2() {
        List<Car> list = new ArrayList<>();
        list.add(new Car("A", 1));
        list.add(new Car("B", 2));
        list.add(new Car("C", 3));
        Map<String, Car> carMap = list.stream().collect(Collectors.toMap(Car::getName, car -> car));
        System.out.println(carMap);
    }

    /**
     * 处理重复数据 包含重复数据的时候,只保留最新的一条
     */
    static void sample3() {
        List<Car> list = new ArrayList<>(4);
        list.add(new Car("A", 1));
        list.add(new Car("A", 2));
        list.add(new Car("B", 2));
        list.add(new Car("C", 3));
        Map<String, Integer> carMap = list.stream().collect(Collectors.toMap(Car::getName, Car::getNum, (oldData, newData) -> newData));
        System.out.println(carMap);
    }

    /**
     * 重复数据,包含重复数据的时候,只保留最新的一条,并把结果保存到ConcurrentHashMap
     */
    static void sample4() {
        List<Car> list = new ArrayList<>();
        list.add(new Car("A", 1));
        list.add(new Car("A", 2));
        list.add(new Car("B", 2));
        list.add(new Car("C", 3));
        Map<String, Integer> carMap = list.stream().collect(Collectors.toMap(Car::getName, Car::getNum, (oldData, newData) -> newData, ConcurrentHashMap::new));
        System.out.println(carMap.getClass());
    }
}
class Car {
   private String name;
   private Integer num;
   // 省略get/set和构造方法
}

forEach 遍历

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Stream;

/**
 * for each遍历方式
 * @since 1.8
 */
public class ForEachTest {
    // jdk8之前常规遍历操作
    static void normForEach() {
        List<String> list = Arrays.asList("a", "b", "c");
        for (String item : list) {
            System.out.println(item);
        }
    }

    static void newForEach() {
        List<String> list = Arrays.asList("a", "b", "c");
        list.forEach(System.out::println);
        list.forEach(s -> {
            System.out.println("新的遍历方式");
            System.out.println(s);
        });
    }

    // Map的遍历 jdk1.8之前
    static void mapNormForEach() {
        Map<Integer, String> map = new HashMap<>(3);
        map.put(1, "a");
        map.put(2, "b");
        map.put(3, "c");
        for (Map.Entry<Integer, String> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ":\t" + entry.getValue());
        }
    }

    //jdk1.8新的Map遍历方法
    static void mapNewForEach() {
        Map<Integer, String> map = new HashMap<>(3);
        map.put(1, "a");
        map.put(2, "b");
        map.put(3, "c");
        map.forEach((k, v) -> {
            System.out.println(k);
            System.out.println(v);
        });
    }

    // jdk1.8新增数组的遍历方法
    static void arrayForEach() {
        String[] array = {"a", "b", "c"};
        Arrays.stream(array).forEach(System.out::println);
    }

    //不保证有序
    static void parallelForEach() {
        Stream<String> stream = Stream.of("ab", "bc", "cd");
        stream.parallel().forEach(System.out::println);
    }

    // 可以保证有序
    static void parallelForEachOrder() {
        Stream<String> stream = Stream.of("ab", "bc", "cd");
        stream.parallel().forEachOrdered(System.out::println);
    }

    // 使用consumer
    static void forEachUseConsumer() {
        Stream<String> s = Stream.of("ab", "bc");
        List<String> l = Arrays.asList("ab", "cd");

        Consumer<String> consumer = s1 -> {
            System.out.println(s1.toUpperCase());
        };
        s.forEach(consumer);
        l.forEach(consumer);
    }
}

Optional

用于优雅判断空。

import java.util.Optional;

/**
 * Optional用法
 * @since 1.8
 */
public class OptionalTest {
    static void handleNull() {
        //String s = null;
        //Optional<String> s1 = Optional.of(s);
        // System.out.println(s1.isPresent());
        Optional<String> hello = Optional.of("hello");
        Optional<Object> empty = Optional.empty();
        Optional<Object> nullObj = Optional.ofNullable(null);
        System.out.println(hello.isPresent());
        System.out.println(empty.isPresent());
        System.out.println(nullObj.isPresent());

    }

    static void emptyGetException() {
        try {
            Optional<String> hello = Optional.of("hello");
            System.out.println(hello.get());
            Optional<Object> empty = Optional.empty();
            System.out.println(empty.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void orElseException() {
        try {
            Optional<String> emptyOptional = Optional.empty();
            String value = emptyOptional.orElseThrow(() -> new Exception("发现空值"));
            System.out.println(value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void orElseGet() {
        Optional<Object> empty = Optional.empty();
        Object o = empty.orElseGet(() -> "default");
        System.out.println(o);
        Object aDefault = empty.orElse("default");
        System.out.println(aDefault);
    }

    static void funcOptional() {
        Optional<Integer> optional123 = Optional.of(123);
        optional123.filter(num -> num == 123).ifPresent(num -> System.out.println(num));

        Optional<Integer> optional456 = Optional.of(456);
        optional456.filter(num -> num == 123).ifPresent(num -> System.out.println(num));

        // map 转换
        Optional<Integer> optional789 = Optional.of(789);
        optional789.map(String::valueOf).map(String::length).ifPresent(length -> System.out.println(length));
    }
}

新的时间处理类

package git.snippets.jdk8;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

import static java.time.temporal.ChronoUnit.DAYS;
/**
 * @since 1.8
 */
//参考 https://www.wdbyte.com/2019/10/jdk/jdk8-time/
public class LocalDateTest {
    static void errorDate() {
        // 不合法的日期
        LocalDate invalidDate = LocalDate.of(2021, 2, 29);
        invalidDate.minusYears(1);
        System.out.println(invalidDate.minusYears(1));
    }

    static void until() {
        LocalDate birthday = LocalDate.of(1989, 9, 27);
        System.out.println(birthday.until(LocalDate.now(), DAYS));
    }

    // 有时区的精确时间
    static void zone() {
        ZonedDateTime nowZone = LocalDateTime.now().atZone(ZoneId.systemDefault());
        System.out.println("当前精确时间(有时区):" + nowZone);
        System.out.println("当前精确时间(有时区):" + nowZone.getYear() + "-" + nowZone.getMonthValue() + "-" + nowZone.getDayOfMonth() + " " + nowZone.getHour() + "-" + nowZone.getMinute() + "-" + nowZone.getSecond());
    }

    static void createTime() {
        LocalDateTime ofTime = LocalDateTime.of(2019, 10, 1, 8, 8, 8);
        System.out.println("当前精确时间:" + ofTime);

        LocalDate localDate = LocalDate.of(2019, 10, 01);
        System.out.println("当前日期:" + localDate);

        LocalTime localTime = LocalTime.of(12, 01, 01);
        System.out.println("当天时间:" + localTime);
    }
}

stream

数据源获取方法 数据处理方法 结果处理方法
Collection.stream ():从集合获取流
Collection.parallelStream ():从集合获取并行流。
Arrays.stream (T array) or Stream.of ():从数组获取流。
BufferedReader.lines ():从输入流中获取流。
IntStream.of ():从静态方法中获取流。
Stream.generate ():自己生成流
map (mapToInt, flatMap等)、 filterdistinctsortedpeeklimitskipparallelsequentialunordered forEachforEachOrderedtoArrayreducecollectminmaxcountanyMatchallMatchnoneMatchfindFirstfindAnyiterator

先看一个最简单的示例



    private static void generateHandle() {
        // 不使用流操作
         List<String> names = Arrays.asList("A", "BBBB", "CCCC", "D");
        // 筛选出长度为4的名字
        List<String> subList = new ArrayList<>();
        for (String name : names) {
            if (name.length() == 4) {
                subList.add(name);
            }
        }
        // 把值用逗号分隔
        StringBuilder sbNames = new StringBuilder();
        for (int i = 0; i < subList.size() - 1; i++) {
            sbNames.append(subList.get(i));
            sbNames.append(", ");
        }
        // 去掉最后一个逗号
        if (subList.size() > 1) {
            sbNames.append(subList.get(subList.size() - 1));
        }
        System.out.println(sbNames);
    }

stream来做,就简洁很多

private static void useStream() {
        List<String> names = Arrays.asList("A", "BBBB", "CCCC", "D");
        String nameString = names.stream()
                .filter(num -> num.length() == 4)
                .collect(Collectors.joining(", "));
        System.out.println(nameString);
    }
package git.snippets.jdk8;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * stream使用
 * 数据源(source) -> 数据处理 / 转换(intermedia) -> 结果处理(terminal )
 * @author <a href="mailto:410486047@qq.com">Grey</a>
 * @date 2021/11/21
 * @since 1.8
 */
public class StreamTest {
    


    static void demo1() {
        List<String> nameList = Arrays.asList("A", "B", "AASDSD", "ABCD");
        nameList.stream()
                .filter(name -> name.length() == 4)
                .map(name -> "This is " + name)
                .forEach(System.out::println);
    }

    static void mathTest() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        IntSummaryStatistics stats = list.stream().mapToInt(x -> x).summaryStatistics();
        System.out.println("最小值:" + stats.getMin());
        System.out.println("最大值:" + stats.getMax());
        System.out.println("个数:" + stats.getCount());
        System.out.println("和:" + stats.getSum());
        System.out.println("平均数:" + stats.getAverage());
    }

    static void groupByTest() {
        List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
        Map<String, List<Integer>> groupMap = ageList.stream()
                .collect(Collectors.groupingBy(age -> String.valueOf(age / 10)));
        groupMap.forEach((k, v) -> {
            System.out.println("年龄" + k + "0多岁的有:" + v);
        });
    }

    static void partitioningByTest() {
        List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
        Map<Boolean, List<Integer>> ageMap = ageList.stream()
                .collect(Collectors.partitioningBy(age -> age > 18));
        System.out.println("未成年人:" + ageMap.get(false));
        System.out.println("成年人:" + ageMap.get(true));
    }

    static void generateTest() {
        // 生成自己的随机数流
        Random random = new Random();
        Stream<Integer> generateRandom = Stream.generate(random::nextInt);
        generateRandom.limit(5).forEach(System.out::println);
        // 生成自己的 UUID 流
        Stream<UUID> generate = Stream.generate(UUID::randomUUID);
        generate.limit(5).forEach(System.out::println);
    }
}

函数式接口

有且仅有一个抽象方法的接口就是函数式接口。可以通过@FunctionalInterface标识,对于只有单个抽象方法(single abstract method)的接口,需要这种接口的对象时,就可以提供Lambda表达式。

最简单的一个函数式接口示例:

@FunctionalInterface
public interface FunctionDemo {
    void say(String name, int age);

    default void hi(String name, int age) {
        say(name, age);
    }
}

package git.snippets.jdk8;

public class FunctionInterfaceDemo {
    public static void main(String[] args) {
        function0();
    }

    // 最简单的函数式接口
    static void function0() {
        FunctionDemo demo = (name, age) -> System.out.println("我叫" + name + "我今年" + age + "岁");
        demo.say("zhangsan", 20);
        demo.hi("zhanshang", 20);
    }
}

Predicate

可以过滤出满足条件的特定元素

示例:

package git.snippets.jdk8;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @author <a href="mailto:410486047@qq.com">Grey</a>
 * @date 2021/11/24
 * @since 1.8
 */
public class PredicateTest {
    public static void main(String[] args) {
        List<Integer> numberList = Arrays.asList(3, 4, 5, 6, 7, 8, 9, 10);
        Predicate<Integer> lessThan5 = number -> number <= 5;
        Predicate<Integer> greaterThan9 = number -> number >= 9;
        // 小于等于 5
        System.out.println(filter(numberList, lessThan5));
        // 大于 5
        System.out.println(filter(numberList, lessThan5.negate()));
        // 小于等于 5 或者大于等于 9
        System.out.println(filter(numberList, lessThan5.or(greaterThan9)));
        // ! (小于等于 5 AND 大于等于 9)
        System.out.println(filter(numberList, lessThan5.and(greaterThan9).negate()));
    }

    // 过滤出满足条件(条件可以自定义)的特定集合元素
    private static <T> List<T> filter(List<T> numberList, Predicate<T> p) {
        List<T> result = new ArrayList<>();
        for (T t : numberList) {
            if (p.test(t)) {
                result.add(t);
            }
        }
        return result;
    }
}

Consumer

Consumer有如下类型

类型 作用
BiConsumer 传入两个任意类型参数,无返回值
DoubleConsumer 传入一个 double 参数,无返回值
IntConsumer 传入一个 int 参数,无返回值
LongConsumer 传入一个 long 参数,无返回值
ObjDoubleConsumer 传入一个任意类型参数,一个 double 参数,无返回值
ObjIntConsumer 传入一个任意类型参数,一个 int 参数,无返回值
ObjLongConsumer 传入一个任意类型参数,一个 long 参数,无返回值

示例代码

package git.snippets.jdk8;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.ObjIntConsumer;

/**
 * Consumer测试
 * <p>
 * BiConsumer 传入两个任意类型参数,无返回值
 * <p>
 * DoubleConsumer 传入一个 double 参数,无返回值
 * <p>
 * IntConsumer 传入一个 int 参数,无返回值
 * <p>
 * LongConsumer 传入一个 long 参数,无返回值
 * <p>
 * ObjDoubleConsumer 传入一个任意类型参数,一个 double 参数,无返回值
 * <p>
 * ObjIntConsumer 传入一个任意类型参数,一个 int 参数,无返回值
 * <p>
 * ObjLongConsumer 传入一个任意类型参数,一个 long 参数,无返回值
 *
 * @author <a href="mailto:410486047@qq.com">Grey</a>
 * @date 2021/11/26
 * @since 1.8
 */
public class ConsumerTest {


    public static void main(String[] args) {
        t1();
        t2();
        t3();
        t4();
    }

    // 多个Consumer结合使用
    static void t1() {
        Consumer<String> c = System.out::println;
        Consumer<String> len = s -> System.out.print(s.length());
        len.andThen(c).accept("hello");
    }

    private static void t4() {
        List<String> list = Arrays.asList("ab", "abcd");
        // 某个字符串串的长度大于给定的value值,就打印
        list.forEach(s -> {
            if (s.length() > 3) {
                System.out.println(s);
            }
        });
    }

    // 打印map中的value满足条件的key值
    private static void t3() {
        Map<String, Integer> map = new HashMap<>();
        map.put("zhangshang", 17);
        map.put("list", 21);
        map.put("wangwu", 18);
        BiConsumer<String, Integer> consumer = (s, i) -> {
            // value大于18的记录,打印其value值
            if (i > 18) {
                System.out.println(s);
            }
        };
        map.forEach(consumer);
    }


    private static void t2() {
        List<String> list = Arrays.asList("ab", "cd");
        // 打印字符串
        list.forEach(System.out::println);

        // 打印每个字符串的长度
        list.forEach(s -> System.out.println(s.length()));
    }
}

Supplier

Supplier是一个功能接口,代表结果的提供者。Supplier的功能方法是get()。一个Supplier可以通过lambda表达式、方法引用或默认构造函数来实例化。

示例代码如下

    private static void s1() throws InterruptedException {
        // 定义一个Supplier,可以生成区间为[0,10)的随机数
        Supplier<Integer> supplier = () -> new Random().nextInt(10);
        System.out.println(supplier.get());
        System.out.println(supplier.get());
        // 获取当前时间
        Supplier<LocalDateTime> s2 = LocalDateTime::now;
        System.out.println(s2.get());
        Thread.sleep(1000);
        System.out.println(s2.get());
    }

此外,使用Supplier,还可以优雅实现工厂模式,见

package git.snippets.jdk8;

import java.time.LocalDateTime;
import java.util.Random;
import java.util.UUID;
import java.util.function.Supplier;

/**
 * Supplier使用
 *
 * @author <a href="mailto:410486047@qq.com">Grey</a>
 * @date 2021/11/27
 * @since
 */
public class SupplierTest {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(factory(() -> new Sharp("abc")));
    }
    // supplier实现工厂模式
    static Sharp factory(Supplier<? extends Sharp> supplier) {
        Sharp sharp = supplier.get();
        sharp.name = sharp.name + UUID.randomUUID();
        return sharp;
    }
}


class Sharp {
    String name;

    Sharp(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Sharp{" + "name='" + name + '\'' + '}';
    }
}

UnaryOperator

UnaryOperator接受一个参数并返回与其输入参数相同类型的结果。

示例代码

package git.snippets.jdk8;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;

/**
 * UnaryOperator使用
 *
 * @author <a href="mailto:410486047@qq.com">Grey</a>
 * @date 2021/11/28
 * @see UnaryOperator
 * @since 1.8
 */
public class UnaryOperatorTest {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("abcddd", "12233243");
        // 将List元素先转大写,然后截取前3位,最后打印出来
        mapAndConsumer(list, System.out::println, String::toUpperCase, s -> s.substring(0, 3));
        unaryOperator2();
    }

    // 接收多个`UnaryOperator`对List元素进行处理,得到的结果执行传入consumer中
    public static <T> void mapAndConsumer(List<T> list, Consumer<T> consumer, UnaryOperator<T>... unaryOperator) {
        for (T t : list) {
            for (UnaryOperator<T> operator : unaryOperator) {
                t = operator.apply(t);
            }
            consumer.accept(t);
        }
    }

    static void unaryOperator2() {
        Function<String, String> upperFun1 = String::toUpperCase;
        UnaryOperator<String> upperFun2 = String::toUpperCase;
        List<String> list = Arrays.asList("abc", "efg");
        // Function.identity() 和  UnaryOperator.identity()都不对结果进行任何操作
        Map<String, String> map1 = list.stream().collect(Collectors.toMap(upperFun1, Function.identity()));
        Map<String, String> map2 = list.stream().collect(Collectors.toMap(upperFun2, UnaryOperator.identity()));
        Map<String, String> map3 = list.stream().collect(Collectors.toMap(upperFun2, t -> t));
        System.out.println(map1);
        System.out.println(map2);
        System.out.println(map3);
    }
}

惰性计算

package git.snippets.jdk8;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 惰性计算
 *
 * @author <a href="mailto:410486047@qq.com">Grey</a>
 * @date 2021/11/23
 * @since 1.8
 */
public class LazyTest {
    public static void main(String[] args) {
        lazyTest();
    }

    private static void lazyTest() {
        List<Integer> numberLIst = Arrays.asList(1, 2, 3, 4, 5, 6);
        // 找出偶数
        Stream<Integer> integerStream = numberLIst.stream()
                .filter(number -> {
                    int temp = number % 2;
                    if (temp == 0) {
                        System.out.println(number);
                    }
                    return temp == 0;
                });
        System.out.println("分割线");
        // 到这里才调用
        List<Integer> collect = integerStream.collect(Collectors.toList());
    }
}

如上代码,打印的结果是:

分割线
2
4
6

说明调用filter的过程是在integerStream.collect(Collectors.toList());执行才触发,这就是惰性计算。

JUC 包中的 CompletableFuture

其中的anyOf()可以实现“任意个CompletableFuture只要一个成功”,allOf()可以实现“所有CompletableFuture都必须成功”,这些组合操作可以实现非常复杂的异步流程控制。

用法参考如下代码

package git.snippets.juc;

import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * 假设你能够提供一个服务
 * 这个服务查询各大电商网站同一类产品的价格并汇总展示
 */
public class CompletableFutureUsage {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        way1();
        way2();
    }

    public static void way1() {
        long start = System.currentTimeMillis();
        System.out.println("p1 " + priceOfJD());
        System.out.println("p2 " + priceOfTB());
        System.out.println("p3 " + priceOfTM());
        long end = System.currentTimeMillis();
        System.out.println("串行执行,耗时(ms):" + (end - start));
    }

    public static void way2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        CompletableFuture<Double> p1 = CompletableFuture.supplyAsync(() -> priceOfJD());
        CompletableFuture<Double> p2 = CompletableFuture.supplyAsync(() -> priceOfTB());
        CompletableFuture<Double> p3 = CompletableFuture.supplyAsync(() -> priceOfTM());
        CompletableFuture.allOf(p1, p2, p3).join();
        System.out.println("p1 " + p1.get());
        System.out.println("p2 " + p2.get());
        System.out.println("p3 " + p3.get());
        long end = System.currentTimeMillis();
        System.out.println("使用CompletableFuture并行执行,耗时(ms): " + (end - start));
    }

    private static double priceOfTM() {
        delay();
        return 1.00;
    }

    private static double priceOfTB() {
        delay();
        return 2.00;
    }

    private static double priceOfJD() {
        delay();
        return 3.00;
    }

    /*private static double priceOfAmazon() {
        delay();
        throw new RuntimeException("product not exist!");
    }*/

    private static void delay() {
        int time = new Random().nextInt(500);
        try {
            TimeUnit.MILLISECONDS.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // System.out.printf("After %s sleep!\n", time);
    }
}

JUC 包中的 WorkStealingPool

每个线程都有单独的队列,每个线程队列执行完毕后,就会去其他的线程队列里面拿过来执行, 底层是:ForkJoinPool,会自动启动cpu核数个线程去执行任务

示例代码

package git.snippets.juc;

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class WorkStealingPoolUsage {
    public static void main(String[] args) throws IOException {
        int core = Runtime.getRuntime().availableProcessors();
        //  会自动启动cpu核数个线程去执行任务 ,其中第一个是1s执行完毕,其余都是2s执行完毕,
        //  有一个任务会进行等待,当第一个执行完毕后,会再次偷取最后一个任务执行
        ExecutorService service = Executors.newWorkStealingPool();
        service.execute(new R(1000));
        for (int i = 0; i < core; i++) {
            service.execute(new R(2000));
        }
        //由于产生的是精灵线程(守护线程、后台线程),主线程不阻塞的话,看不到输出
        System.in.read();
    }

    static class R implements Runnable {

        int time;

        R(int t) {
            this.time = t;
        }

        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(time);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(time + " " + Thread.currentThread().getName());
        }
    }
}

更多

Java SE 7及以后各版本新增特性,持续更新中...

参考资料

Java 新特性教程

Java Language Updates

Enhancements in Java SE 8

New and Enhanced APIs That Take Advantage of Lambda Expressions and Streams in Java SE 8

Java多线程学习笔记

Spring in Action, 5th

Java核心技术·卷 I(原书第11版)

Java核心技术·卷 II(原书第11版)

posted @ 2022-08-13 17:38  Grey Zeng  阅读(559)  评论(1编辑  收藏  举报