Java8函数式编程2-类库、高级集合类和收集器

参考并建议阅读《Java8函数式编程》
转载请注明出处:http://blog.csdn.net/cuiods/article/details/53691534


一、类库

1、基本类型

Java的泛型是是基于对泛型参数类型的擦除,只有装箱类型才能作为泛型参数,然而,由于装箱类型是对象,所以在内存中占据更大的空间,比如int只要4个字节,而Integer确需要16个字节。
将基本类型转换为装箱类型称为装箱,反之成为拆箱,两者都需要额外的计算开销。为了减小这些性能开销, Stream 类的某些方法对基本类型和装箱类型做了区分。
对基本类型做特殊处理的方法在命名上有明确的规范。 如果方法返回类型为基本类型, 则在基本类型前加 To,如ToLongFunction。这些基本类型都有与之对应的 Stream, 以基本类型名为前缀, 如 LongStream。

public static void printTrackLengthStatistics(Album album) {
    IntSummaryStatistics trackLengthStats
                       = album.getTracks()
                              .mapToInt(track -> track.getLength())
                              .summaryStatistics();
    System.out.printf("Max: %d, Min: %d, Ave: %f, Sum: %d",
                       trackLengthStats.getMax(),
                       trackLengthStats.getMin(),
                       trackLengthStats.getAverage(),
                       trackLengthStats.getSum());
}

2、@FunctionalInterface

每个用作函数接口的接口都应该添加@FunctionalInterface注释。
并不是所有只有一个方法的接口都可以用lambda表达式来实现,有些接口不可以用作函数接口,例如java.lang.comparable接口,函数之间是不可比较的,因为在通常意义下我们不认为函数有属性或状态。
@FunctionalInterface会强制检查一个接口是否符合函数接口的标准,如果该注释添加给一个枚举类或另一个注释,或接口不止一个方法,javac都会报错。

3、默认方法

Java8中Collection接口中添加了stream()方法,在Java类库中实现Collection接口的类会需要添加该方法的实现。但是在用Java1-7编写的程序中,用户实现Collection接口的类则因为没有实现stream()而无法编译。为了不违反向下兼容的原则,java提供了这些接口的默认实现,成为默认方法
Iterable中默认方法的示例:

default void forEach(Consumer<? super T> action) {
    for (T t : this) {
        action.accept(t);
    }
}

4、多重继承

接口允许多重继承,在遇到两个接口包含签名相同的默认方法时,javac不明确应该继承哪个接口的中的方法,因此编译器会报错。但是如果在类中再次实现该方法则能解决该问题。
三定律:

  1. 类胜于接口。如果在继承链中有方法体或抽象的方法声明, 那么就可以忽略接口中定义的方法。
  2. 子类胜于父类。 如果一个接口继承了另一个接口, 且两个接口都定义了一个默认方法,那么子类中定义的方法胜出。
  3. 没有规则三。如果上面两条规则不适用, 子类要么需要实现该方法, 要么将该方法声明为抽象方法。

二、高级集合类和收集器

1、方法引用

lambda表达式有一个常见的用法,如

artist -> artist.getName()

Java8为其提供了一个简写语法,叫做方法引用,以上方法调用可以写为:

Artist::getName

标准语法为Classname::methodName。对于构造函数

(name, nationality) -> new Artist(name, nationality)

可以改写为

Artist::new

2、使用收集器

收集器是一种通用的、从流生成复杂值的结构。将收集器传给collect方法,则可以从流生成不同的数据结构。
除了前面介绍的toList,还有toSet和toCollection,分别生成set和collection的实例。Stream类库在背后自动选择合适的List和Set类型。
除了类库的提供的数据类型,还可以转换为自定义的集合类型:

stream.collect(toCollection(TreeSet::new));

收集器也可以让流生成一个值,比如minBy和maxBy允许按某种特定的顺序生成一个值。

public Optional<Artist> biggestGroup(Stream<Artist> artists) {
    Function<Artist,Long> getCount = artist -> artist.getMembers().count();
    return artists.collect(maxBy(comparing(getCount)));
}

除此之外还可以对数据分组:

public Map<Artist, List<Album>> albumsByArtist(Stream<Album> albums) {
     return albums.collect(groupingBy(album -> album.getMainMusician()));
}

3、一些细节

关于Map类,一种通常的用法是使用Map类定义缓存,传统的处理方式是先试着从 Map 中取值, 如果没有取到, 创建一个新值并返回。

public Artist getArtist(String name) {
    Artist artist = artistCache.get(name);
    if (artist == null) {
        artist = readArtistFromDB(name);
        artistCache.put(name, artist);
    }
    return artist;
}

Java8为Map引入了新方法computeIfAbsent,使用该方法可以将上面的代码重写为

public Artist getArtist(String name) {
    return artistCache.computeIfAbsent(name, this::readArtistFromDB);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值