Java 8 结论

本篇内容

  • Java 8 的新特新以及其对编程风格颠覆性的影响。
  • 由Java 8萌生的一些尚未成熟的编程思想。
  • Java 9以及Java 10 可能发生的变化。
1.回顾 Java8的语言特性
  • 对多核处理器处理能力的需求日益增长,虽然硅开发技术也在不断进步,但依据摩尔定律每年新增的晶体管数量已经无法使独立CPU核的速度更快了。简单来说,要让你的代码运行得更快,需要你的代码具备并行运算的能力。
  • 更简洁地调度以显示风格处理数据的数据集合,这一趋势不断增长。比如,创建一些数据源,抽象所有数据以符合给定的标准,给结果运用一些操作,而不是概括结果或者将结果组成集合以后再做进一步处理。这一风格与使用不变对象和集合相关,它们之后会进一步生成不变值。
1.1 行为参数化(Lambda 以及方法引用)
1.2 流
1.3 CompletableFuture
1.4 Optional
1.5 默认方法
2.其他语言特性的更新
2.1 注解
  • 你现在可以定义重复注解
  • 使用新版Java,你可以为任何类型添加注解
2.2 通用目标类型推断
3.类库的更新
3.1 其他新增的方法
类/接口新方法
MapgetOrDefault,forEach,compute,computeIfAbsent,computeIfPresent,merge, putIfAbsent,remove(key,value),replace,replaceAll
IterableforEach,spliterator
IteratorforEachRemaining
CollectionremoveIf,stream,parallelStream
ListreplaceAll,sort
BitSetstream
3.1.1 Map

    Map接口的变化最大,它增加了多个新方法,利用这些新方法能更加便利地操纵Map中的数据。比如,getOrDefault方法就可以替换现在检测Map中是否包含给定键映射的惯用方法。如
果Map中不存在这样的键映射,你可以提供一个默认值,方法会返回该默认值。
    另一个特别有用的方法是computeIfAbsent,它能帮助你非常方便地使用缓存模式。

public String getData(String url){ 
 String data = cache.get(url); 
 if(data == null){ 
   data = getData(url); 
   cache.put(url, data); 
 } 
 return data;
}

    这段代码,你现在可以通过computeIfAbsent用更加精炼的方式实现,代码如下所示:

public String getData(String url){ 
 return cache.computeIfAbsent(url, this::getData); 
}
3.1.2 集合

    removeIf方法可以移除集合中满足某个谓词的所有元素。注意,这一方法与我们在介绍Stream API时提到的filter方法不大一样。Stream API中的filter方法会产生一个新的流,不会对当前作为数据源的流做任何变更。

3.1.3 列表

    replaceAll方法会对列表中的每一个元素执行特定的操作,并用处理的结果替换该元素。它的功能和Stream中的map方法非常相似,不过replaceAll会修改列表中的元素。与此相反,map方法会生成新的元素。

3.2 Collections 类

    Collections类已经存在了很长的时间,它的主要功能是操作或者返回集合。Java 8中它又新增了一个方法,该方法可以返回不可修改的、同步的、受检查的或者是空的NavigableMap或NavigableSet。除此之外,它还引入了checkedQueue方法,该方法返回一个队列视图,可以扩展进行动态类型检查。

3.3 Comparator 类

    Comparator接口现在同时包含了默认方法和静态方法。

  • reversed—— 对当前的 Comparator 对象进行逆序排序,并返回排序之后新的Comparator对象。
  • thenComparing——当两个对象相同时,返回使用另一个Comparator进行比较的Comparator对象。
  • thenComparingInt、thenComparingDouble、thenComparingLong——这些方法的工作方式和thenComparing方法类似,不过它们的处理函数是特别针对某些基本数据类型(分别对应于ToIntFunction、ToDoubleFunction和ToLongFunction)的。
3.4 并发

    Arrays类现在支持并发操作了。
    java.util.concurrent.atomic

3.4.1 原子操作

    java.util.concurrent.atomic包提供了多个对数字类型进行操作的类,比如AtomicInteger和AtomicLong,它们支持对单一变量的原子操作。这些类在Java 8中新增了更多的方
法支持。

  1. getAndUpdate——以原子方式用给定的方法更新当前值,并返回变更之前的值。
  2. updateAndGet——以原子方式用给定的方法更新当前值,并返回变更之后的值。
  3. getAndAccumulate——以原子方式用给定的方法对当前及给定的值进行更新,并返回
    变更之前的值。
  4. accumulateAndGet——以原子方式用给定的方法对当前及给定的值进行更新,并返回
    变更之后的值。
3.4.2 Adder和Accumulator

    多线程的环境中,如果多个线程需要频繁地进行更新操作,且很少有读取的动作(比如,在统计计算的上下文中),Java API文档中推荐大家使用新的类LongAdder、LongAccumulator、Double-Adder以及DoubleAccumulator,尽量避免使用它们对应的原子类型。这些新的类在设计之初就考虑了动态增长的需求,可以有效地减少线程间的竞争。
    LongAddr和DoubleAdder类都支持加法操作,而LongAccumulator和DoubleAccumulator可以使用给定的方法整合多个值。比如,可以像下面这样使用LongAdder计算多个值的总和。

LongAdder adder=new LongAdder();
adder.add(10);
adder.sum();
		
LongAccumulator acc=new LongAccumulator(Long::sum, 0);
acc.accumulate(10);
acc.get();
3.4.3 ConcurrentHashMap

    ConcurrentHashMap类的引入极大地提升了HashMap现代化的程度,新引入的ConcurrentHashMap对并发的支持非常友好。ConcurrentHashMap允许并发地进行新增和更新操作,因为它仅对内部数据结构的某些部分上锁。因此,和另一种选择,即同步式的Hashtable比较起来,它具有更高的读写性能。
性能
    为了改善性能,要对ConcurrentHashMap的内部数据结构进行调整。典型情况下,map的条目会被存储在桶中,依据键生成哈希值进行访问。但是,如果大量键返回相同的哈希值,由于桶是由List实现的,它的查询复杂度为O(n),这种情况下性能会恶化。在Java 8中,当桶过于臃肿时,它们会被动态地替换为排序树(sorted tree),新的数据结构具有更好的查询性能(排序树的查询复杂度为O(log(n)))。注意,这种优化只有当键是可以比较的(比如String或者Number类)时才可能发生。
类流操作
    ConcurrentHashMap支持三种新的操作,这些操作和你之前在流中所见的很像:

  • forEach——对每个键值对进行特定的操作
  • reduce——使用给定的精简函数(reduction function),将所有的键值对整合出一个结果
  • search——对每一个键值对执行一个函数,直到函数的返回值为一个非空值
        以上每一种操作都支持四种形式,接受使用键、值、Map.Entry以及键值对的函数:
  • 使用键和值的操作(forEach、reduce、search)
  • 使用键的操作(forEachKey、reduceKeys、searchKeys)
  • 使用值的操作 (forEachValue、reduceValues、searchValues)
  • 使用Map.Entry对象的操作(forEachEntry、reduceEntries、searchEntries)

    下面这个例子中,我们使用reduceValues试图找出map中的最大值:

ConcurrentHashMap<String,Integer> map=new ConcurrentHashMap<>();
Optional<Integer> maxValue = Optional.of(map.reduceValues(1, Integer::max));

计数
    ConcurrentHashMap类提供了一个新的方法,名叫mappingCount,它以长整型long返回map中映射的数目。我们应该尽量使用这个新方法,而不是老的size方法,size方法返回的类型为int。这是因为映射的数量可能是int无法表示的。

集合视图
    ConcurrentHashMap类还提供了一个名为KeySet的新方法,该方法以Set的形式返回ConcurrentHashMap的一个视图(对map的修改会反映在该Set中,反之亦然)。你也可以使用新的静态方法newKeySet,由ConcurrentHashMap创建一个Set。

3.4.4 Arrays

    Arrays类提供了不同的静态方法对数组进行操作。现在,它又包括了四个新的方法(它们都有特别重载的变量)。

3.4.4.1 使用 parallelSort

    parallelSort方法会以并发的方式对指定的数组进行排序,你可以使用自然顺序,也可以为数组对象定义特别的Comparator。

3.4.4.2 使用 setAll 和 parallelSetAll

    setAll和parallelSetAll方法可以以顺序的方式也可以用并发的方式,使用提供的函数计算每一个元素的值,对指定数组中的所有元素进行设置。该函数接受元素的索引,返回该索引
元素对应的值。由于parallelSetAll需要并发执行,所以提供的函数必须没有任何副作用。

		int[] numbers = new int[10];
		Arrays.setAll(numbers, i -> i * 3);
3.4.4.13 使用 parallelPrefix

    parallelPrefix方法以并发的方式,用用户提供的二进制操作符对给定数组中的每个元素进行累积计算。通过下面这段代码,你会得到这样的一些值:1, 2, 3, 4, 5, 6, 7, …。

		int[] ones = new int[10];
		Arrays.fill(ones, 1);
		Arrays.parallelPrefix(ones, (a, b) -> a + b);
3.5 Number 和 Math

    Java 8 API对Number和Math也做了改进,为它们增加了新的方法。
Number
    Number类中新增的方法如下。

  • Short、Integer、Long、Float和Double类提供了静态方法sum、min和max。
  • Integer和Long类提供了compareUnsigned、divideUnsigned、remainderUnsigned和toUnsignedLong方法来处理无符号数。
  • Integer和Long类也分别提供了静态方法parseUnsignedInt和parseUnsignedLong将字符解析为无符号int或者long类型。
  • Byte和Short类提供了toUnsignedInt和toUnsignedLong方法通过无符号转换将参数转化为 int 或 者 long 类型。类似地, Integer 类现在也提供了静态方法toUnsignedLong。
  • Double和Float类提供了静态方法isFinite,可以检查参数是否为有限浮点数。
  • Boolean类现在提供了静态方法logicalAnd、logicalOr和logicalXor,可以在两个boolean之间执行and、or和xor操作。
  • BigInteger 类提供了 byteValueExact 、 shortValueExact 、 intValueExact 和longValueExact,可以将BigInteger类型的值转换为对应的基础类型。不过,如果在转换过程中有信息的丢失,方法会抛出算术异常。

Math
    如果Math中的方法在操作中出现溢出,Math类提供了新的方法可以抛出算术异常。支持这一异常的方法包括使用int和long参数的addExact、subtractExact、multipleExact、incrementExact、decrementExact和negateExact。此外,Math类还新增了一个静态方法toIntExact,可以将long值转换为int值。其他的新增内容包括静态方法floorMod、floorDiv和nextDown。

3.6 Files

    Files类最引人注目的改变是,你现在可以用文件直接产生流。静态方法Files.lines,通过该方法你可以以延迟方式读取文件的内容,并将其作为一个流。此外,还有一些非常有用的静态方法可以返回流。

  • Files.list——生成由指定目录中所有条目构成的Stream<Path>。这个列表不是递归包含的。由于流是延迟消费的,处理包含内容非常庞大的目录时,这个方法非常有用。
  • Files.walk——和Files.list有些类似,它也生成包含给定目录中所有条目的Stream<Path>。不过这个列表是递归的,你可以设定递归的深度。注意,该遍历是依照深度优先进行的。
  • Files.find—— 通过递归地遍历一个目录找到符合条件的条目,并生成一个Stream<Path>对象。
3.7 Reflection
3.8 String

    String类也新增了一个静态方法,名叫join。你大概已经猜出它的功能了,它可以用一个分隔符将多个字符串连接起来。你可以像下面这样使用它:

String join = String.join(",", "1", "2");
System.out.println(join);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值