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::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
用于优雅判断空和
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