Java SE 8 新增特性
Java SE 8 新增特性
作者:Grey
原文地址:
源码
镜像仓库: GitCode:java_new_features
Lambda 表达式
Java 8里面最大的更新莫过于支持Lambda表达式,Oracle官网给了一个很好的示例说明,见:Lambda Expressions,以下来自这个官方示例说明。
假设我们定义一个Person类,属性如下
public class Person {
    public static List<Person> createRoster() {
        // 获取Person列表
    }
    public enum Sex {
        MALE, FEMALE
    }
    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;
    // 省略get/set方法
    public void printPerson() {
        // 打印Person信息
    }
}
如果我们要获取某个年龄段的所有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接口
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());
还可以转换成Anonymous Class的写法
printPersons(roster,new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);
接下来,这种方式就可以转换成Lambda表达式的写法
printPersons(
    roster,
    (Person p) -> p.getGender() == Person.Sex.MALE
        && p.getAge() >= 18
        && p.getAge() <= 25
);
由于CheckPerson这个接口只有一个方法,所以这又是一个函数式接口(Fuctional Interface),在我们这个例子的场景下,我们可以用Predicate<T>来替换CheckPerson接口。因为Predicate<T>接口就是
interface Predicate<T> {
    boolean test(T 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::compareByAgeMethodReferencesExamples::appendStrings | 
| 实例方法 | *containingObject*::*instanceMethodName* | myComparisonProvider::compareByNamemyApp::appendStrings2 | 
| 对一个特定类型的任意对象的实例方法的引用 | *ContainingType*::*methodName* | String::compareToIgnoreCaseString::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 | AutoCloseableThreadLocalStringIterableCharSequenceBooleanIntegerLongFloatDouble | 
| java.nio.file | not applicable | Files | 
| java.util | PrimitiveIteratorSpliteratorDoubleSummaryStatisticsIntSummaryStatisticsLongSummaryStatisticsOptionalOptionalDoubleOptionalIntOptionalLongSpliteratorsSplittableRandomStringJoiner | ArraysBitSetCollectionComparatorIteratorListMapMap.EntryLinkedHashMapRandomTreeMap | 
| 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
用于优雅判断空和
null。
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等)、filter、distinct、sorted、peek、limit、skip、parallel、sequential、unordered | forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny、iterator | 
先看一个最简单的示例
    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标识
最简单的一个函数式接口示例:
@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());
        }
    }
}
更多
参考资料
New and Enhanced APIs That Take Advantage of Lambda Expressions and Streams in Java SE 8
Java SE 8 新增特性的更多相关文章
- Java SE 9 新增特性
		Java SE 9 新增特性 作者:Grey 原文地址: Java SE 9 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new_ ... 
- Java SE 10 新增特性
		Java SE 10 新增特性 作者:Grey 原文地址:Java SE 10 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ... 
- Java SE 11 新增特性
		Java SE 11 新增特性 作者:Grey 原文地址:Java SE 11 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ... 
- Java SE 12 新增特性
		Java SE 12 新增特性 作者:Grey 原文地址:Java SE 12 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ... 
- Java SE 13 新增特性
		Java SE 13 新增特性 作者:Grey 原文地址:Java SE 13 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ... 
- Java SE 14 新增特性
		Java SE 14 新增特性 作者:Grey 原文地址:Java SE 14 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ... 
- Java SE 15 新增特性
		Java SE 15 新增特性 作者:Grey 原文地址:Java SE 15 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ... 
- Java SE 16 新增特性
		Java SE 16 新增特性 作者:Grey 原文地址:Java SE 16 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ... 
- Java SE 17 新增特性
		Java SE 17 新增特性 作者:Grey 原文地址:Java SE 17 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ... 
随机推荐
- 《回炉重造 Java 基础》——集合(容器)
			整体框架 绿色代表接口/抽象类:蓝色代表类. 主要由两大接口组成,一个是「Collection」接口,另一个是「Map」接口. 前言 以前刚开始学习「集合」的时候,由于没有好好预习,也没有学好基础知识 ... 
- QC快速充电
			QC快充 一.高通QC快充的介绍 二.识别充电类型的芯片介绍 三.QC充电曲线 四.如何在log中看QC充电类型 五.QC3识别错误 六.波形图 一.高通QC快充的介绍 高通QC快充技术,又称Quic ... 
- CSS3:scrollbar样式设置
			CSS3:scrollbar样式设置 1. 设置出现滚动条的方式 overflow:scroll --- x和y方向都会出现滚动条 或者 overflow-x:scroll --- 只有x方向出现滚动 ... 
- 解决跨海高并发崩溃难题?so easy
			近年来随着互联网强势的发展浪潮,越来越多的企业选择跨境出海,扩展海外市场.而想要在一个陌生市场最快速地吸引到用户,一定不能缺少的就是丰富多样的各类活动.然而活动在带来大流量的同时,也带来了一些问题,比 ... 
- Visual Studio Installer下载速度为0处理办法
			DNS改为:223.5.5.5即可. 223.5.5.5 下载完成后记得改回来. 
- Leetcode 1051. 高度检查器
			这题的目的是找出排序后和排序前位置不同的元素的个数 正常通过复制出一个新的数组,然后对比排序后的数组就能做出,但是时间是1ms 然后发现一种基于桶排序来计数的做法 public int heightC ... 
- MongoDB 的内存使用限制
			本文将简述一下MongoDB的内存限制问题 1. 使用Docker限制 当我们使用docker创建mongo 容器时,可通过使用以下参数,对mongo可以使用的资源进行限制 内存限制 参数 简介 -m ... 
- 由ASP.NET Core根据路径下载文件异常引发的探究
			前言 最近在开发新的项目,使用的是ASP.NET Core6.0版本的框架.由于项目中存在文件下载功能,没有使用类似MinIO或OSS之类的分布式文件系统,而是下载本地文件,也就是根据本地文件路径进行 ... 
- adb工具
			ADB:全称为Android Debug Bridge,它是 Android 开发/测试人员不可替代的强大工具. 首先,下载ADB工具并安装: 下载:百度就有.下载后是个压缩包,将其拷贝到cm ... 
- FileNameFilter过滤器的使用和Lambda优化程序--IO概述(概念&分类)
			FileNameFilter过滤器的使用和Lambda优化程序 public class Demo02Filter { public static void main(String[] args) { ... 
