Java 8 简明教程
“Java is still not dead–and people are starting to fingure that out”.
接口默认方法
java 8 可以在接口中使用default
定义默认方法。可以多个
example:1
2
3
4
5
6
7
interface Formula {
double calculate (int a) ;
default double sqrt (int a) {
return Math.sqrt(a);
}
}
Lambda 表达式
1
2
3
4
5
6
7
8
List<String> names = Arrays.asList("peter" , "anna" , "mike" , "xenia" );
Collections.sort(names, new Comparator<String>() {
@Override
public int compare (String a, String b) {
return b.compareTo(a);
}
});
1
2
3
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
1
Collections.sort(names, (String a, String b) -> b.compareTo(a));
1
names.sort((a, b) -> b.compareTo(a));
函数接口
一个Lamda表达式对应于一个函数接口中的抽象方法。
一个函数接口有且只有一个抽象方法。
@FunctionalInterface
(省略不报错)注解告诉编译器这是函数接口,以保证只有一个抽象方法。
Example:
1
2
3
4
@FunctionalInterface
interface Converter <F , T > {
T convert (F from) ;
}
1
2
3
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123" );
System.out.println(converted);
方法和构造函数引用
java 8 允许使用::
关键字引用对象的方法和构造函数
引用静态方法
1
2
3
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123" );
System.out.println(converted);
引用非静态方法
1
2
3
4
5
class Something {
String startsWith (String s) {
return String.valueOf(s.charAt(0 ));
}
}
1
2
3
4
Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java" );
System.out.println(converted);
1
2
3
4
5
6
7
8
9
10
11
class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this .firstName = firstName;
this .lastName = lastName;
}
}
Java编译器会根据函数接口方法的参数,自动选择符合声明的构造方法。1
2
3
interface PersonFactory <P extends Person > {
P create (String firstName, String lastName) ;
}
1
2
PersonFactory<Person> personFactory = Person::new ;
Person person = personFactory.create("Peter" , "Parker" );
Lambda 表达式的范围 与匿名内部类类似, 你可以在Lambda表达式中访问final局部变量
、实例变量
以及静态变量
。
访问局部变量 1
2
3
4
5
final int num = 1 ;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2 );
与匿名内部类不同的是,被Lambda表达式使用的变量默认为final,可以省略final。
1
2
3
4
5
int num = 1 ;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
stringConverter.convert(2 );
num
默认final,以下代码num = 3
会编译错误。
1
2
3
4
int num = 1 ;
Converter<Integer, String> stringConverter =
(from) -> String.valueOf(from + num);
num = 3 ;
访问实例变量和静态变量 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Lambda4 {
static int outerStaticNum;
int outerNum;
void testScopes () {
Converter<Integer, String> stringConverter1 = (from) -> {
outerNum = 23 ;
return String.valueOf(from);
};
Converter<Integer, String> stringConverter2 = (from) -> {
outerStaticNum = 72 ;
return String.valueOf(from);
};
}
}
访问函数接口默认方法
默认方法不能 通过Lambda表达式调用。1
Formula formula = (a) -> sqrt(a * 100 );
Java 8 内置函数接口
Predicate (判断)
Predicate 接受一个参数,返回boolean类型。
定义
1
2
3
4
5
6
7
8
9
10
11
12
@FunctionalInterface
public interface Predicate <T > {
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test (T t) ;
...
example
1
2
3
4
5
6
7
8
9
10
Predicate<String> predicate = (s) -> s.length() > 0 ;
predicate.test("foo" );
predicate.negate().test("foo" );
Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
Functions (函数)
Functions 接受一个参数,返回一个处理后的参数。
定义
1
2
3
4
5
6
7
8
9
10
11
@FunctionalInterface
public interface Function <T , R > {
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply (T t) ;
...
example
1
2
3
4
Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);
backToString.apply("123" );
Suppliers (生产者)
Suppliers 不接受参数,返回一个对象。
定义
1
2
3
4
5
6
7
8
9
10
@FunctionalInterface
public interface Supplier <T > {
* Gets a result.
*
* @return a result
*/
T get () ;
}
example
1
2
Supplier<Person> personSupplier = Person::new ;
personSupplier.get();
Consumers (消费者)
Consumers 接受一个参数,不返回。
定义
1
2
3
4
5
6
7
8
9
10
@FunctionalInterface
public interface Consumer <T > {
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept (T t) ;
...
example
1
2
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke" , "Skywalker" ));
Comparator (比较器)
java 8 增加了默认方法
定义
1
2
3
4
5
@FunctionalInterface
public interface Comparator <T > {
int compare (T o1, T o2) ;
...
example
1
2
3
4
5
6
7
Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
Person p1 = new Person("John" , "Doe" );
Person p2 = new Person("Alice" , "Wonderland" );
comparator.compare(p1, p2);
comparator.reversed().compare(p1, p2);
Optional
Optionals 可以避免 NullPointerException
.
Optional是一个容器,空或者非空。 example
1
2
3
4
5
6
7
Optional<String> optional = Optional.of("bam" );
optional.isPresent();
optional.get();
optional.orElse("fallback" );
optional.ifPresent((s) -> System.out.println(s.charAt(0 )));
Streams ( 流)
java.util.Stream
可以执行一系列的流操作。
java.util.Collection
like lists or sets (maps are not supported) 可以转化成Stream
.
Stream 可以并行执行。
1
2
3
4
5
6
7
8
9
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" );
Filter
Filter 接受一个Predicate,根据Predicate过滤集合,这是一个中间操作。
1
2
3
4
5
6
stringCollection
.stream()
.filter((s) -> s.startsWith("a" ))
.forEach(System.out::println);
Sorted
Sorted 是中间操作,默认按自然顺序排序,可以接受一个Comparator
。
Sorted 不会影响原来集合的排序。
1
2
3
4
5
6
7
stringCollection
.stream()
.sorted()
.filter((s) -> s.startsWith("a" ))
.forEach(System.out::println);
1
2
System.out.println(stringCollection);
Map
Map 是中间操作,接受一个Function
,一对一的映射。
1
2
3
4
5
6
7
stringCollection
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> b.compareTo(a))
.forEach(System.out::println);
Match
Match是一个终止操作,接受Predicate
,返回boolean。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
boolean anyStartsWithA =
stringCollection
.stream()
.anyMatch((s) -> s.startsWith("a" ));
System.out.println(anyStartsWithA);
boolean allStartsWithA =
stringCollection
.stream()
.allMatch((s) -> s.startsWith("a" ));
System.out.println(allStartsWithA);
boolean noneStartsWithZ =
stringCollection
.stream()
.noneMatch((s) -> s.startsWith("z" ));
System.out.println(noneStartsWithZ);
Count
Count 是一个终止操作,计算返回的元素个数,返回long
。
1
2
3
4
5
6
7
long startsWithB =
stringCollection
.stream()
.filter((s) -> s.startsWith("b" ))
.count();
System.out.println(startsWithB);
Reduce
Reduce是终止操作,接受BiFunction
。返回Optional
。
1
2
3
4
5
6
7
8
Optional<String> reduced =
stringCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
Collect
将流转成集合的终止操作,Collectors.toList()
, Collectors.toSet()
.
1
2
3
4
5
6
7
List<Person> filtered =
persons
.stream()
.filter(p -> p.name.startsWith("P" ))
.collect(Collectors.toList());
System.out.println(filtered);
分组 Collectors.groupingBy()
1
2
3
Map<Integer, List<Person>> personsByAge = persons
.stream()
.collect(Collectors.groupingBy(p -> p.age));
Collections.summaizeingInt()
1
2
3
4
5
6
7
IntSummaryStatistics ageSummary =
persons
.stream()
.collect(Collectors.summarizingInt(p -> p.age));
System.out.println(ageSummary);
Collections.joining()
;
1
2
3
4
5
String phrase = persons
.stream()
.filter(p -> p.age >= 18 )
.map(p -> p.name)
.collect(Collectors.joining(" and " , "In Germany " , " are of legal age." ));
Collection.toMap()
1
2
3
4
5
6
7
8
9
Map<Integer, String> map = persons
.stream()
.collect(Collectors.toMap(
p -> p.age,
p -> p.name,
(name1, name2) -> name1 + ";" + name2));
System.out.println(map);
自定义Collection
1
2
3
4
5
6
7
8
9
10
11
12
Collector<Person, StringJoiner, String> personNameCollector =
Collector.of(
() -> new StringJoiner(" | " ),
(j, p) -> j.add(p.name.toUpperCase()),
(j1, j2) -> j1.merge(j2),
StringJoiner::toString);
String names = persons
.stream()
.collect(personNameCollector);
System.out.println(names);
FlatMap 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Streams7 {
static class Foo {
String name;
List<Bar> bars = new ArrayList<>();
Foo(String name) {
this .name = name;
}
}
static class Bar {
String name;
Bar(String name) {
this .name = name;
}
}
public static void main (String[] args) {
test2();
}
static void test2 () {
IntStream.range(1 , 4 )
.mapToObj(num -> new Foo("Foo" + num))
.peek(f -> IntStream.range(1 , 4 )
.mapToObj(num -> new Bar("Bar" + num + " <- " + f.name))
.forEach(f.bars::add))
.flatMap(f -> f.bars.stream())
.forEach(b -> System.out.println(b.name));
}
static void test1 () {
List<Foo> foos = new ArrayList<>();
IntStream
.range(1 , 4 )
.forEach(num -> foos.add(new Foo("Foo" + num)));
foos.forEach(f ->
IntStream
.range(1 , 4 )
.forEach(num -> f.bars.add(new Bar("Bar" + num + " <- " + f.name))));
foos.stream()
.flatMap(f -> f.bars.stream())
.forEach(b -> System.out.println(b.name));
}
}
Optinal的flatMap使用1
2
3
4
5
Optional.of(new Outer())
.flatMap(o -> Optional.ofNullable(o.nested))
.flatMap(n -> Optional.ofNullable(n.inner))
.flatMap(i -> Optional.ofNullable(i.foo))
.ifPresent(System.out::println);
Stream 进阶 其他的Stream 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Stream.of("a1" , "a2" , "a3" )
.findFirst()
.ifPresent(System.out::println);
IntStream.range(1 , 4 )
.forEach(System.out::println);
Arrays.stream(new int [] {1 , 2 , 3 })
.map(n -> 2 * n + 1 )
.average()
.ifPresent(System.out::println);
Stream.of(1.0 , 2.0 , 3.0 )
.mapToInt(Double::intValue)
.mapToObj(i -> "a" + i)
.forEach(System.out::println);
Stream处理顺序
Stream 重复使用
一旦Stream终止,流就会关闭,不能再使用。1
2
3
4
5
6
Stream<String> stream =
Stream.of("d2" , "a2" , "b1" , "b3" , "c" )
.filter(s -> s.startsWith("a" ));
stream.anyMatch(s -> true );
stream.noneMatch(s -> true );
1
2
3
4
5
java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at java.util.stream.ReferencePipeline.noneMatch(ReferencePipeline.java:459)
at com.winterbe.java8.Streams5.test7(Streams5.java:38)
at com.winterbe.java8.Streams5.main(Streams5.java:28)
解决方法:用中间过程的流创建一个Supplier
,返回Stream
。
1
2
3
4
5
6
Supplier<Stream<String>> streamSupplier =
() -> Stream.of("d2" , "a2" , "b1" , "b3" , "c" )
.filter(s -> s.startsWith("a" ));
streamSupplier.get().anyMatch(s -> true );
streamSupplier.get().noneMatch(s -> true );
Parallel Streams (并行流) First we create a large list of unique elements:
1
2
3
4
5
6
int max = 1000000 ;
List<String> values = new ArrayList<>(max);
for (int i = 0 ; i < max; i++) {
UUID uuid = UUID.randomUUID();
values.add(uuid.toString());
}
Now we measure the time it takes to sort a stream of this collection.
Sequential Sort 1
2
3
4
5
6
7
8
9
10
11
long t0 = System.nanoTime();
long count = values.stream().sorted().count();
System.out.println(count);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("sequential sort took: %d ms" , millis));
Parallel Sort 1
2
3
4
5
6
7
8
9
10
11
long t0 = System.nanoTime();
long count = values.parallelStream().sorted().count();
System.out.println(count);
long t1 = System.nanoTime();
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("parallel sort took: %d ms" , millis));
As you can see both code snippets are almost identical but the parallel sort is roughly 50% faster. All you have to do is change stream()
to parallelStream()
.
Maps
Map
不能直接转化成Stream
,map.keySet().stream()
, map.values().stream()
and map.entrySet().stream()
.
Map
1
2
3
4
5
6
7
Map<Integer, String> map = new HashMap<>();
for (int i = 0 ; i < 10 ; i++) {
map.putIfAbsent(i, "val" + i);
}
map.forEach((id, val) -> System.out.println(val));
1
2
3
4
5
6
7
8
9
10
11
map.computeIfPresent(3 , (num, val) -> val + num);
map.get(3 );
map.computeIfPresent(9 , (num, val) -> null );
map.containsKey(9 );
map.computeIfAbsent(23 , num -> "val" + num);
map.containsKey(23 );
map.computeIfAbsent(3 , num -> "bam" );
map.get(3 );
Next, we learn how to remove entries for a given key, only if it’s currently mapped to a given value:
1
2
3
4
5
map.remove(3 , "val3" );
map.get(3 );
map.remove(3 , "val33" );
map.get(3 );
Another helpful method:
1
map.getOrDefault(42 , "not found" );
Merging entries of a map is quite easy:
1
2
3
4
5
map.merge(9 , "val9" , (value, newValue) -> value.concat(newValue));
map.get(9 );
map.merge(9 , "concat" , (value, newValue) -> value.concat(newValue));
map.get(9 );
Merge either put the key/value into the map if no entry for the key exists, or the merging function will be called to change the existing value.
Date API Java 8 引入新的日期和时间接口, java.time
.新的日期接口与Joda-Time library 兼容, however it’s not the same .
Clock Clock 提供当前的时间和日期,是带时区的。 可以替代 System.currentTimeMillis()
。 Instant
表示即使时间,可以转 java.util.Date
objects.
1
2
3
4
5
Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();
Instant instant = clock.instant();
Date legacyDate = Date.from(instant);
Timezones 1
2
3
4
5
6
7
8
9
10
System.out.println(ZoneId.getAvailableZoneIds());
ZoneId zone1 = ZoneId.of("Europe/Berlin" );
ZoneId zone2 = ZoneId.of("Brazil/East" );
System.out.println(zone1.getRules());
System.out.println(zone2.getRules());
LocalTime 1
2
3
4
5
6
7
8
9
10
LocalTime now1 = LocalTime.now(zone1);
LocalTime now2 = LocalTime.now(zone2);
System.out.println(now1.isBefore(now2));
long hoursBetween = ChronoUnit.HOURS.between(now1, now2);
long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);
System.out.println(hoursBetween);
System.out.println(minutesBetween);
1
2
3
4
5
6
7
8
9
10
LocalTime late = LocalTime.of(23 , 59 , 59 );
System.out.println(late);
DateTimeFormatter germanFormatter =
DateTimeFormatter
.ofLocalizedTime(FormatStyle.SHORT)
.withLocale(Locale.GERMAN);
LocalTime leetTime = LocalTime.parse("13:37" , germanFormatter);
System.out.println(leetTime);
LocalDate 1
2
3
4
5
6
7
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plus(1 , ChronoUnit.DAYS);
LocalDate yesterday = tomorrow.minusDays(2 );
LocalDate independenceDay = LocalDate.of(2014 , Month.JULY, 4 );
DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();
System.out.println(dayOfWeek);
1
2
3
4
5
6
7
DateTimeFormatter germanFormatter =
DateTimeFormatter
.ofLocalizedDate(FormatStyle.MEDIUM)
.withLocale(Locale.GERMAN);
LocalDate xmas = LocalDate.parse("24.12.2014" , germanFormatter);
System.out.println(xmas);
LocalDateTime 1
2
3
4
5
6
7
8
9
10
LocalDateTime sylvester = LocalDateTime.of(2014 , Month.DECEMBER, 31 , 23 , 59 , 59 );
DayOfWeek dayOfWeek = sylvester.getDayOfWeek();
System.out.println(dayOfWeek);
Month month = sylvester.getMonth();
System.out.println(month);
long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);
System.out.println(minuteOfDay);
1
2
3
4
5
6
Instant instant = sylvester
.atZone(ZoneId.systemDefault())
.toInstant();
Date legacyDate = Date.from(instant);
System.out.println(legacyDate);
1
2
3
4
5
6
7
DateTimeFormatter formatter =
DateTimeFormatter
.ofPattern("MMM dd, yyyy - HH:mm" );
LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13" , formatter);
String string = formatter.format(parsed);
System.out.println(string);
Unlike java.text.NumberFormat
the new DateTimeFormatter
is immutable and thread-safe .
Annotations Annotations in Java 8 are repeatable. Let’s dive directly into an example to figure that out.
First, we define a wrapper annotation which holds an array of the actual annotations:
1
2
3
4
5
6
7
8
@interface Hints {
Hint[] value();
}
@Repeatable (Hints.class)
@interface Hint {
String value () ;
}
Java 8 enables us to use multiple annotations of the same type by declaring the annotation @Repeatable
.
Variant 1: Using the container annotation (old school) 1
2
@Hints ({@Hint ("hint1" ), @Hint ("hint2" )})
class Person {}
Variant 2: Using repeatable annotations (new school) 1
2
3
@Hint ("hint1" )
@Hint ("hint2" )
class Person {}
Using variant 2 the java compiler implicitly sets up the @Hints
annotation under the hood. That’s important for reading annotation information via reflection.
1
2
3
4
5
6
7
8
Hint hint = Person.class.getAnnotation(Hint.class);
System.out.println(hint);
Hints hints1 = Person.class.getAnnotation(Hints.class);
System.out.println(hints1.value().length);
Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);
System.out.println(hints2.length);
Although we never declared the @Hints
annotation on the Person
class, it’s still readable via getAnnotation(Hints.class)
. However, the more convenient method is getAnnotationsByType
which grants direct access to all annotated @Hint
annotations.
Furthermore the usage of annotations in Java 8 is expanded to two new targets:
1
2
@Target ({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@interface MyAnnotation {}