Java SE 8 新增特性
Java SE 8 新增特性
作者:Grey
原文地址:
源码
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 等)、 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
标识,对于只有单个抽象方法(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());
}
}
}
更多
参考资料
New and Enhanced APIs That Take Advantage of Lambda Expressions and Streams in Java SE 8
本文来自博客园,作者:Grey Zeng,转载请注明原文链接:https://www.cnblogs.com/greyzeng/p/16583640.html