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() {
	@Override
	public int compare(Human h1, Human h2) {
		return h1.getName().compareTo(h2.getName());
	}
}
@Test
public void givenPreLambda_whenSortingEntitiesByName_thenCorrectlySorted() {
	List humans = Lists.newArrayList(new Human("Sarah", 10), new Human("Jack", 12));
	Collections.sort(humans, new Comparator() {
		@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 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),也可以是原始整数 (IntStream)、长整型 (LongStream) 或双精度 (DoubleStream)。
// 使用传统的for循环的方式:
// 将每个userId查询到的记录放在List中
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> userIDandBaseInfoMap = userBaseInfoList.stream()       
                               .collect(Collectors.groupingBy(UserBaseInfo::getUserId));  // 说明:Collection接口的默认方法创建流     

创建stream的方式:
  • Stream接口的静态工厂方法of (Java8里接口可以带静态方法)
  • Collection接口默认方法或子类获取流(Java8对Collection的增强)(见上文例子)
使用Stream接口创建流:
// stream接口的of方法有两个重载的方法
Stream stringStream = Stream.of("youzan");
 
Stream integerStream = Stream.of(1, 2, 3, 5);
 
 
// 使用变长参数创建流返回的是一个Arrays的流(源码)
@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static Stream of(T... values) {
    return Arrays.stream(values);
}
 
 
// generator方法的方式
Stream.generate(new Supplier() {
    @Override
    public Double get() {
        return Math.random();
    }
});
Stream.generate(() -> Math.random());
Stream.generate(Math::random);

Java8对Collection接口的增强
public interface Collection extends Iterable {
    default Stream stream() { // 接口中新增了Stream方法,让集合类可以很方便的创建流
        return StreamSupport.stream(spliterator(), false);
    }
}

使用Stream接口创建流:
// stream接口的of方法有两个重载的方法
Stream stringStream = Stream.of("youzan");

Stream integerStream = Stream.of(1, 2, 3, 5);


// 使用变长参数创建流返回的是一个Arrays的流(源码)
@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static Stream of(T... values) {
    return Arrays.stream(values);
}


// generator方法的方式
Stream.generate(new Supplier() {
	@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 f) 创建一个包含序列 first, f(first), f(f(first)), ...的无限流
   
Stream.generate(Supplier f) 使用一个生成器函数创建一个无限流
IntStream.range(lower, upper) 创建一个由下限到上限(不含)之间的元素组成的IntStream
IntStream.rangeClosed(lower, upper) 创建一个由下限到上限(含)之间的元素组成的 IntStream
BufferedReader.lines() 创建一个有来自 BufferedReader 的行组成的流
BitSet.stream() 创建一个由 BitSet 中的设置位的索引组成的 IntStream
Stream.chars() 创建一个与 String 中的字符对应的 IntStream


流的中间操作:
操作 内容
filter(Predicate) 与预期匹配的流的元素
map(Function) 将提供的函数应用于流的元素的结果
flatMap(Function> 将提供的流处理函数应用于流元素后获得的流元素
distinct() 已删除了重复的流元素
sorted() 按自然顺序排序的流元素
Sorted(Comparator) 按提供的比较符排序的流元素
limit(long) 截断至所提供长度的流元素
skip(long) 丢弃了前 N 个元素的流元素
takeWhile(Predicate) (仅限 Java 9)在第一个提供的预期不是 true 的元素处阶段的流元素
dropWhile(Predicate) (仅限 Java 9)丢弃了所提供的预期为 true 的初始元素分段的流元素


流的终止操作:
操作 内容
forEach(Consumer action) 将提供的操作应用于流的每个元素
toArray() 使用流的元素创建一个数组
reduce(...) 将流的元素聚合为一个汇总值
collect(...) 将流的元素聚合到一个汇总结果容器中
min(Comparator) 通过比较符返回流的最小元素
max(Comparator) 通过比较符返回流的最大元素
count() 返回流的大小
{any,all,none}Match(Predicate) 返回流的任何/所有元素是否与提供的预期相匹配
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 optional = Optional.of("youzan");


// 使用of方法,不会报NPE
Optional optional = Optional.ofNullable("youzan");


// 通过源码来看为什么of方法会报NPE
public static  Optional of(T value) {
    return new Optional<>(value); // 新建一个对象
}
// 调用显式构造函数去新建对象
private Optional(T value) {
    this.value = Objects.requireNonNull(value);
}
// Objects工具类的requireNonNull方法遇到null会报NPE
public static  T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}


// 接下来还是通过源码看看ofNullable方法是怎么样做到允许null的
public static  Optional ofNullable(T value) {
    return value == null ? empty() : of(value); // 如果value为null则调用empty方法
}
// 隐藏在empty方法中的秘密
public static Optional empty() {
    @SuppressWarnings("unchecked")
    Optional t = (Optional) 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 other) {
    return value != null ? value : other.get();
}

orElseThrow方法:如果对象值为null则抛出一个异常
// 使用姿势
Optional.ofNullable(object).orElseThrow(NoSuchElementException::new);


// orElseThrow方法源码
public  T orElseThrow(Supplier exceptionSupplier) throws X {
    if (value != null) {
        return value;
    } else {
        throw exceptionSupplier.get(); // 嗯,这里能帮你抛出一个异常就是了
    }
}

map方法:map方法用来对Optional实例的值执行一系列操作。通过一组实现了Function接口的lambda表达式传入操作。
// 使用姿势
Optional upperName = name.map((value) -> value.toUpperCase());


// map方法源码
public Optional map(Function 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 name = Optional.of("Sana");
Optional longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters")); // 输出Sanaulla





你可能感兴趣的:(java)