1、λ表达式
lambda表达式:λ表达式是Java8新增的一个特性,《Core Java》中的对它的解析是——“一个可传递的代码块,可以在以后执行一次或多次”。
lambda表达式的语法:
(parameters) → expression
或者
(parameters) → { statements; } // 这种方式可以写多条语句
- 从日常开发的角度来看,它可以简化我们的很多代码(当然不止这一个原因),特别是很多匿名内部类的写法都可以被λ表达式替换成一个语句。
- λ表达式从本质上来看是语法糖,但它并不是简单的匿名内部类的语法糖,λ表达式的内部实现机制也都不是采用匿名内部类,说到底还是性能原因。
- 对于大多数情况来说,Lambda表达式要比匿名内部类性能更优。
// 使用匿名类的方式新建一个线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello world !");
}
}).start();
// 使用λ表达式的方式新建一个线程
new Thread(() -> System.out.println("Hello world !")).start();
lambda表达式的语法:
(parameters) → expression
或者
(parameters) → { statements; } // 这种方式可以写多条语句
lambda表达式的使用场景:
- 匿名内部类(上文已展示)
- 集合排序
- 结合stream使用
public class Human {
private String name;
private int age;
public Human() {
super();
}
// standard getters and setters……
}
// 使用传统的方式对集合进行排序:对集合进行排序要为Comparator创建一个匿名内部类用来排序:
new Comparator<Human>() {
@Override
public int compare(Human h1, Human h2) {
return h1.getName().compareTo(h2.getName());
}
}
@Test
public void givenPreLambda_whenSortingEntitiesByName_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
Collections.sort(humans, new Comparator<Human>() {
@Override
public int compare(Human h1, Human h2) {
return h1.getName().compareTo(h2.getName());
}
});
Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
// 使用λ表达式方式
@Test
public void whenSortingEntitiesByName_thenCorrectlySorted() {
List<Human> humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
humans.sort((Human h1, Human h2) -> h1.getName().compareTo(h2.getName()));
Assert.assertThat(humans.get(0), equalTo(new Human("Jack", 12)));
}
2、Stream
Stream(流)是Java8的新增的一个库,它最常见的用法是对集合中的数据进行操作,它就像一个高级版的Iterator,使用者只需要定义好对元素的操作,并将操作应用到元素上就好,而具体的实现算法则交给类库的设计者,这样就很方便的
将操作和算法分开。所有流计算都有一种共同的结构:它们具有
一个流来源、0 或多个中间操作,以及一个终止操作。流的元素可以是对象引用 (Stream<String>),也可以是原始整数 (IntStream)、长整型 (LongStream) 或双精度 (DoubleStream)。
// 使用传统的for循环的方式:
// 将每个userId查询到的记录放在List<UserBaseInfo>中
for (UserBaseInfo userBaseInfo : userBaseInfoList) {
userId = userBaseInfo.getUserId();
if (userIDandBaseInfoMap.containsKey(userId)) {
userBaseInfos = userIDandBaseInfoMap.get(userId);
} else {
userBaseInfos = new ArrayList<>();
userIDandBaseInfoMap.put(userId, userBaseInfos);
}
userBaseInfos.add(userBaseInfo);
}
// 使用stream进行操作
Map<Long, List<UserBaseInfo>> userIDandBaseInfoMap = userBaseInfoList.stream()
.collect(Collectors.groupingBy(UserBaseInfo::getUserId)); // 说明:Collection接口的默认方法创建流
- Stream接口的静态工厂方法of (Java8里接口可以带静态方法)
- Collection接口默认方法或子类获取流(Java8对Collection的增强)(见上文例子)
使用Stream接口创建流:
// stream接口的of方法有两个重载的方法
Stream<String> stringStream = Stream.of("youzan");
Stream<Integer> integerStream = Stream.of(1, 2, 3, 5);
// 使用变长参数创建流返回的是一个Arrays的流(源码)
@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
// generator方法的方式
Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
});
Stream.generate(() -> Math.random());
Stream.generate(Math::random);
Java8对Collection接口的增强
public interface Collection<E> extends Iterable<E> {
default Stream<E> stream() { // 接口中新增了Stream方法,让集合类可以很方便的创建流
return StreamSupport.stream(spliterator(), false);
}
}
// stream接口的of方法有两个重载的方法
Stream<String> stringStream = Stream.of("youzan");
Stream<Integer> integerStream = Stream.of(1, 2, 3, 5);
// 使用变长参数创建流返回的是一个Arrays的流(源码)
@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
// generator方法的方式
Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
});
Stream.generate(() -> Math.random());
Stream.generate(Math::random);
方法 | 描述 |
---|---|
Collection.stream() | 使用一个集合的元素创建一个流 |
Stream.of(T...) | 使用传递给工厂方法的参数创建一个流 |
Stream.of(T[]) | 使用一个数组的元素创建一个流 |
Stream.empty() | 创建一个空流。 |
Stream.iterate(T first, BinaryOperator<T> f) | 创建一个包含序列 first, f(first), f(f(first)), ... 的无限流 |
Stream.generate(Supplier<T> f) | 使用一个生成器函数创建一个无限流 |
IntStream.range(lower, upper) | 创建一个由下限到上限(不含)之间的元素组成的IntStream |
IntStream.rangeClosed(lower, upper) | 创建一个由下限到上限(含)之间的元素组成的 IntStream |
BufferedReader.lines() | 创建一个有来自 BufferedReader 的行组成的流 |
BitSet.stream() | 创建一个由 BitSet 中的设置位的索引组成的 IntStream |
Stream.chars() | 创建一个与 String 中的字符对应的 IntStream |
流的中间操作:
操作 | 内容 |
---|---|
filter(Predicate<T>) | 与预期匹配的流的元素 |
map(Function<T, U>) | 将提供的函数应用于流的元素的结果 |
flatMap(Function<T, Stream<U>> | 将提供的流处理函数应用于流元素后获得的流元素 |
distinct() | 已删除了重复的流元素 |
sorted() | 按自然顺序排序的流元素 |
Sorted(Comparator<T>) | 按提供的比较符排序的流元素 |
limit(long) | 截断至所提供长度的流元素 |
skip(long) | 丢弃了前 N 个元素的流元素 |
takeWhile(Predicate<T>) | (仅限 Java 9)在第一个提供的预期不是 true 的元素处阶段的流元素 |
dropWhile(Predicate<T>) | (仅限 Java 9)丢弃了所提供的预期为 true 的初始元素分段的流元素 |
流的终止操作:
操作 | 内容 |
---|---|
forEach(Consumer<T> action) | 将提供的操作应用于流的每个元素 |
toArray() | 使用流的元素创建一个数组 |
reduce(...) | 将流的元素聚合为一个汇总值 |
collect(...) | 将流的元素聚合到一个汇总结果容器中 |
min(Comparator<T>) | 通过比较符返回流的最小元素 |
max(Comparator<T>) | 通过比较符返回流的最大元素 |
count() | 返回流的大小 |
{any,all,none}Match(Predicate<T>) | 返回流的任何/所有元素是否与提供的预期相匹配 |
findFirst() | 返回流的第一个元素(如果有) |
findAny() | 返回流的任何元素(如果有) |
3、Optional
Optional:Optional是Java8新引进的一个允许为null的容器对象,它的到来可以使程序员减少与NullPointException(NPE)打交道的次数。Optional提供了一种优雅的Java风格的方法来解决null安全问题。
// Java8之前想获取一个实体的某一个属性是这样的
public String getName(Employee em) {
if (em == null) {
return "Unknow employee name";
}
return em.getName();
}
// 使用Optional优雅的获取
public String getName(Employee em) {
Optional.ofNullable(em).map(employee -> employee.getName()).orElse("Unknow employee name");
}
创建Optional对象:
- of方法
- ofNullable方法(推荐)
// 使用of方法,但是它仍然有可能报NPE
Optional<String> optional = Optional.of("youzan");
// 使用of方法,不会报NPE
Optional<String> optional = Optional.ofNullable("youzan");
// 通过源码来看为什么of方法会报NPE
public static <T> Optional<T> of(T value) {
return new Optional<>(value); // 新建一个对象
}
// 调用显式构造函数去新建对象
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
// Objects工具类的requireNonNull方法遇到null会报NPE
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
// 接下来还是通过源码看看ofNullable方法是怎么样做到允许null的
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value); // 如果value为null则调用empty方法
}
// 隐藏在empty方法中的秘密
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY; // 返回一个EMPTY对象
return t;
}
// EMPTY的庐山真面目
private static final Optional<?> EMPTY = new Optional<>();
// Optional提供的无参构造函数将返回一个值空的Optional对象
private Optional() {
this.value = null; // value是用来存储对象的值的
}
orElse方法:对象的值为null则返回参数传进来的值
// 使用姿势
String string = Optional.ofNullable("youzan").orElse("Unknow");
// orElse方法揭秘
public T orElse(T other) {
return value != null ? value : other; // 如果当前的Optional对象为null则返回other
}
orElseGet方法:orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。
// orElseGet方法源码
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
orElseThrow方法:如果对象值为null则抛出一个异常
// 使用姿势
Optional.ofNullable(object).orElseThrow(NoSuchElementException::new);
// orElseThrow方法源码
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get(); // 嗯,这里能帮你抛出一个异常就是了
}
}
// 使用姿势
Optional<String> upperName = name.map((value) -> value.toUpperCase());
// map方法源码
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper); // 如果形参mapper为null,则报NPE
if (!isPresent())
return empty(); // 返回一个不包含值的Optional对象
else {
return Optional.ofNullable(mapper.apply(value));
}
}
// isPresent方法是查看当前optional对象的值是否有值
public boolean isPresent() {
return value != null;
}
flatMap方法:flatMap方法与map方法类似,区别在于mapping函数的返回值不同。map方法的mapping函数返回值可以是任何类型T,而flatMap方法的mapping函数必须是Optional。
filter方法:filter个方法通过传入限定条件对Optional实例的值进行过滤。
// filter方法检查给定的Option值是否满足某些条件。
// 如果满足则返回同一个Option实例,否则返回空Optional。
Optional<String> name = Optional.of("Sana");
Optional<String> longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters")); // 输出Sanaulla