Java8新特性(lambda、Stream、Optional)

1、λ表达式

lambda表达式:λ表达式是Java8新增的一个特性,《Core Java》中的对它的解析是——“一个可传递的代码块,可以在以后执行一次或多次”。
  • 从日常开发的角度来看,它可以简化我们的很多代码(当然不止这一个原因),特别是很多匿名内部类的写法都可以被λ表达式替换成一个语句。
  • λ表达式从本质上来看是语法糖,但它并不是简单的匿名内部类的语法糖,λ表达式的内部实现机制也都不是采用匿名内部类,说到底还是性能原因。
  • 对于大多数情况来说,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的方式:
  • 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接口创建流:
// 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(); // 嗯,这里能帮你抛出一个异常就是了
    }
}

map方法:map方法用来对Optional实例的值执行一系列操作。通过一组实现了Function接口的lambda表达式传入操作。
// 使用姿势
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





猜你喜欢

转载自blog.csdn.net/key_next/article/details/79007076