Java8新特性总结

简介: Java8新特性总结

  一、方法和构造函数引用

Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted); // 123

Java 8 允许你通过::关键字获取方法或者构造函数的的引用。 上面的例子就演示了如何引用一个静态方法。 而且, 我们还可以对一个对象的方法进行引用:

class Something {
  String startsWith(String s) {
  return String.valueOf(s.charAt(0));
 }
}
Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted); // "J"

让我们看看如何使用::关键字引用构造函数。 首先我们定义一个示例bean, 包含不同的构造方法:

class Person {
   String firstName;
   String lastName;
   Person() {}
   Person(String firstName, String lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
   }
}

接下来, 我们定义一个person工厂接口, 用来创建新的person对象:

interface PersonFactory<P extends Person> {
 P create(String firstName, String lastName);
}

然后我们通过构造函数引用来把所有东西拼到一起, 而不是像以前一样, 通过手动实现一个工厂来这么做。

PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");

我们通过Person::new来创建一个Person类构造函数的引用。 Java编译器会自动地选择合适的构造函数来匹配PersonFactory.create函数的签名, 并选择正确的构造函数形式

二、允许在接口中有默认方法实现

interface Formula {
  default double sqrt(int a) {
   return Math.sqrt(a);
  }
}
Formula formula = new Formula() {
  @Override
  public double calculate(int a) {
   return sqrt(a * 100);
  }
};

三、时间日期API

Timezones,LocalTime

四、内置函数式接口

1.Predicate

Predicate是一个布尔类型的函数, 该函数只有一个输入参数。Predicate接口包含了多种默认方法, 用于处理复杂的逻辑动词

Predicate<String> predicate = (s) -> s.length() > 0;
predicate.test("foo"); // true
predicate.negate().test("foo"); // false
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();

2.Function

Function接口接收一个参数, 并返回单一的结果。 默认方法可以将多个函数串在一起( compse, andThen)

Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString =
toInteger.andThen(String::valueOf);
backToString.apply("123"); // "123"

3.Optional

Optional不是一个函数式接口, 而是一个精巧的工具接口, 用来防止NullPointerException产生。 这个概念在下一节会显得很重要, 所以我们在这里快速地浏览一下Optional的工作原理。Optional是一个简单的值容器, 这个值可以是null, 也可以是nonnull。 考虑到一个方法可能会返回一个non-null的值, 也可能返回一个空值。 为了不直接返回null, 我们在Java 8中就返回一个Optional.

Optional<String> optional = Optional.of("bam");
optional.isPresent(); // true
optional.get(); // "bam"
optional.orElse("fallback"); // "bam"
optional.ifPresent((s) -> System.out.println(s.charAt(0))); //"b"

4.Streams

java.util.Stream表示了某一种元素的序列, 在这些元素上可以进行各种操作。 Stream操作可以是中间操作, 也可以是完结操作。 完结操作会返回一个某种类型的值, 而中间操作会返回流对象本身, 并且你可以通过多次调用同一个流操作方法来将操作结果串起来( 就像StringBuffer的append方法一样) 。 Stream是在一个源的基础上创建出来的, 例如java.util.Collection中的list或者set( map不能作为Stream的源) 。 Stream操作往往可以通过顺序或者并行两种方式来执行。我们先了解一下序列流。 首先, 我们通过string类型的list的形式创

建示例数据:

List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");

Java 8中的Collections类的功能已经有所增强, 你可以之直接通过调用Collections.stream()或者

Collection.parallelStream()方法来创建一个流对象。 下面的章节会解释这个最常用的操作。

5.Filter

Filter接受一个predicate接口类型的变量, 并将所有流对象中的元素进行过滤。 该操作是一个中间操作, 因此它允许我们在返回结果的基础上再进行其他的流操作( forEach) 。 ForEach接受一个function接口类型的变量, 用来执行对每一个元素的操作。 ForEach是一个中止r操作 它不返回流, 所以我们不能再调用其他的流操作。

stringCollection
 .stream()
 .filter((s) -> s.startsWith("a"))
 .forEach(System.out::println);
// "aaa2", "aaa1"

6.Sorted

Sorted是一个中间操作, 能够返回一个排过序的流对象的视图。 流对象中的元素会默认按照自然顺序进行排序, 除非你自己指定一个Comparator接口来改变排序规则。

stringCollection
 .stream()
 .sorted()
 .filter((s) -> s.startsWith("a"))
 .forEach(System.out::println);
// "aaa1", "aaa2"

一定要记住, sorted只是创建一个流对象排序的视图, 而不会改变原来集合中元素的顺序。 原来string集合中的元素顺序是没有改变的。

System.out.println(stringCollection);
// ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1

7.Map

map是一个对于流对象的中间操作, 通过给定的方法, 它能够把流对象中的每一个元素对应到另外一个对象上。 下面的例子就演示了如何把每个string都转换成大写的string. 不但如此, 你还可以把每一种对象映射成为其他类型。 对于带泛型结果的流对象, 具体的类型还要由传递给map的泛型方法来决定。

stringCollection
 .stream()
 .map(String::toUpperCase)
 .sorted((a, b) -> b.compareTo(a))
 .forEach(System.out::println);
// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"

8.Match

匹配操作有多种不同的类型, 都是用来判断某一种规则是否与流对象相互吻合的。 所有的匹配操作都是终结操作, 只返回一个boolean类型的结果。

boolean anyStartsWithA =
 stringCollection
 .stream()
 .anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA); // true
boolean allStartsWithA =
stringCollection
 .stream()
 .allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA); // false
boolean noneStartsWithZ = stringCollection
  .stream()
  .noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ); // true

9.Count

Count是一个终结操作, 它的作用是返回一个数值, 用来标识当前流对象中包含的元素数量。

long startsWithB =
stringCollection
    .stream()
    .filter((s) -> s.startsWith("b"))
    .count();
System.out.println(startsWithB); // 3

10.Reduce

该操作是一个终结操作, 它能够通过某一个方法, 对元素进行削减操作。 该操作的结果会放在一个Optional变量里返回。

Optional<String> reduced =
 stringCollection
 .stream()
 .sorted()
 .reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"

11.Parallel Streams

Parallel Streams像上面所说的, 流操作可以是顺序的, 也可以是并行的。 顺序操作通过单线程执行, 而并行操作则通过多线程执行。下面的例子就演示了如何使用并行流进行操作来提高运行效率, 代码非常简单。首先我们创建一个大的list, 里面的元素都是唯一的:

int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
   UUID uuid = UUID.randomUUID();
  values.add(uuid.toString());
}

流可以并行执行, 在大量输入元素上可以提升运行时的性能。 并行流使用公共的 ForkJoinPool , 由 ForkJoinPool.commonPool() 方法提供。 底层线程池的大小最大为五个线程 — 取决于CPU的物理核数。

Arrays.asList("a1", "a2", "b1", "c2", "c1")
 .parallelStream()
 .filter(s -> {
 System.out.format("filter: %s [%s]\n",
 s, Thread.currentThread().getName());
 return true;
})
 .map(s -> {
 System.out.format("map: %s [%s]\n",s, Thread.currentThread().getName());
 return s.toUpperCase();
})
.sorted((s1, s2) -> {
   System.out.format("sort: %s <> %s [%s]\n",s1, s2, Thread.currentThread().getName());
      return s1.compareTo(s2);
})
.forEach(s -> System.out.format("forEach: %s[%s]\n",s,Thread.currentThread().getName()));

输出结果如下:

filter: c2 [ForkJoinPool.commonPool-worker-3]

filter: c1 [ForkJoinPool.commonPool-worker-2]

map: c1 [ForkJoinPool.commonPool-worker-2]

filter: a2 [ForkJoinPool.commonPool-worker-1]

map: a2 [ForkJoinPool.commonPool-worker-1]

filter: b1 [main]

map: b1 [main]

filter: a1 [ForkJoinPool.commonPool-worker-2]

map: a1 [ForkJoinPool.commonPool-worker-2]

map: c2 [ForkJoinPool.commonPool-worker-3]

sort: A2 <> A1 [main]

sort: B1 <> A2 [main]

sort: C2 <> B1 [main]

sort: C1 <> C2 [main]

sort: C1 <> B1 [main]

sort: C1 <> C2 [main]

forEach: A1 [ForkJoinPool.commonPool-worker-1]

forEach: C2 [ForkJoinPool.commonPool-worker-3]

forEach: B1 [main]

forEach: A2 [ForkJoinPool.commonPool-worker-2]

forEach: C1 [ForkJoinPool.commonPool-worker-1]

就像你看到的那样, 并行流使用了所有公共的 ForkJoinPool 中的可用线程来执行流式操作。 在连续的运行中输出可能有所不同, 因为所使用的特定线程是非特定的。

 

sort 只在主线程上串行执行。 实际上, 并行流上的 sort 在背后使用了Java8中新的方法 Arrays.parallelSort() 。

五、处理顺序

数据流操作要么是衔接操作, 要么是终止操作。 衔接操作返回数据流,所以我们可以把多个衔接操作不使用分号来链接到一起。 终止操作无返回值, 或者返回一个不是流的结果。 在上面的例子中, filter 、 map 和 sorted 都是衔接操作, 而 forEach 是终止操作

衔接操作的一个重要特性就是延迟性。 观察下面没有终止操作的例子:

Stream.of("d2", "a2", "b1", "b3", "c")
 .filter(s -> {
 .System.out.println("filter: " + s);
 .return true;
});

执行这段代码时, 不向控制台打印任何东西。 这是因为衔接操作只在终止操作调用时被执行。让我们通过添加终止操作 forEach 来扩展这个例子:

Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
   System.out.println("filter: " + s);
   return true;
})
.forEach(s -> System.out.println("forEach: " + s));

执行这段代码会得到如下输出:

filter: d2
forEach: d2
filter: a2
forEach: a2
filter: b1
forEach: b1
filter: b3
forEach: b3
filter: c
forEach: c

结果的顺序可能出人意料。 原始的方法会在数据流的所有元素上, 一个接一个地水平执行所有操作。 但是每个元素在调用链上垂直移动。 第一个字符串 "d2" 首先经过 filter 然后是 forEach , 执行完后才开始处理第二个字符串 "a2" 。

这种行为可以减少每个元素上所执行的实际操作数量, 就像我们在下个例子中看到的那样:

 

Stream.of("d2", "a2", "b1", "b3", "c")
 .map(s -> {
     System.out.println("map: " + s);
     return s.toUpperCase();
 })
.anyMatch(s -> {
   System.out.println("anyMatch: " + s);
   return s.startsWith("A");
});
// map: d2
// anyMatch: D2
// map: a2
// anyMatch: A2

只要提供的数据元素满足了谓词, anyMatch 操作就会返回 true 。 对于第二个传递 "A2" 的元素, 它的结果为真。 由于数据流的链式调用是垂直执行的, map 这里只需要执行两次。 所以 map 会执行尽可能少的次数, 而不是把所有元素都映射一遍。

下面的例子由两个衔接操作 map 和 filter , 以及一个终止操作 forEach 组成。 让我们再来看看这些操作如何执行:

Stream.of("d2", "a2", "b1", "b3", "c")
 .map(s -> {
 System.out.println("map: " + s);
 return s.toUpperCase();
})
.filter(s -> {
   System.out.println("filter: " + s);
   return s.startsWith("A");
})
.forEach(s -> System.out.println("forEach: " + s));
// map: d2
// filter: D2
// map: a2
// filter: A2
// forEach: A2
// map: b1
// filter: B1
// map: b3
// filter: B3
// map: c
// filter: C

就像你可能猜到的那样, map 和 filter 会对底层集合的每个字符串调用五次, 而 forEach 只会调用一次。如果我们调整操作顺序, 将 filter 移动到调用链的顶端, 就可以极大减少操作的执行次数:

 

Stream.of("d2", "a2", "b1", "b3", "c")
 .filter(s -> {
     System.out.println("filter: " + s);
     return s.startsWith("a");
 })
 .map(s -> {
    System.out.println("map: " + s);
    return s.toUpperCase();
})
.forEach(s -> System.out.println("forEach: " + s));
// filter: d2
// filter: a2
// map: a2
// forEach: A2
// filter: b1
// filter: b3
// filter: c

现在, map 只会调用一次, 所以操作流水线对于更多的输入元素会执行更快。 在整合复杂的方法链时, 要记住这一点


相关文章
|
26天前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
53 2
|
27天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
38 3
|
27天前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
32 2
|
29天前
|
存储 算法 Java
Java Set因其“无重复”特性在集合框架中独树一帜
【10月更文挑战第14天】Java Set因其“无重复”特性在集合框架中独树一帜。本文深入解析Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定的数据结构(哈希表、红黑树)确保元素唯一性,并提供最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的`hashCode()`与`equals()`方法。
28 3
|
1月前
|
安全 Java API
Java 17新特性让你的代码起飞!
【10月更文挑战第4天】自Java 8发布以来,Java语言经历了多次重大更新,每一次都引入了令人兴奋的新特性,极大地提升了开发效率和代码质量。本文将带你从Java 8一路走到Java 17,探索那些能让你的代码起飞的关键特性。
75 1
|
1月前
|
编解码 Oracle Java
java9到java17的新特性学习--github新项目
本文宣布了一个名为"JavaLearnNote"的新GitHub项目,该项目旨在帮助Java开发者深入理解和掌握从Java 9到Java 17的每个版本的关键新特性,并通过实战演示、社区支持和持续更新来促进学习。
77 3
|
9天前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
21 4
|
24天前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
46 3
|
24天前
|
存储 安全 Java
Java Map新玩法:深入探讨HashMap和TreeMap的高级特性
【10月更文挑战第19天】Java Map新玩法:深入探讨HashMap和TreeMap的高级特性,包括初始容量与加载因子的优化、高效的遍历方法、线程安全性处理以及TreeMap的自然排序、自定义排序、范围查询等功能,助你提升代码性能与灵活性。
24 2
|
30天前
|
Java 开发者
在Java的集合世界里,Set以其独特的特性脱颖而出,它通过“哈希魔法”和“红黑树防御”两大绝技
【10月更文挑战第13天】在Java的集合世界里,Set以其独特的特性脱颖而出。它通过“哈希魔法”和“红黑树防御”两大绝技,有效抵御重复元素的侵扰,确保集合的纯洁性和有序性。无论是“人海战术”还是“偷梁换柱”,Set都能从容应对,成为开发者手中不可或缺的利器。
31 6