Java8 总结笔记
Java8 给出了很多新特性,让Java编程更丝滑。如引入Stream流处理、Lambda表达式、默认方法、Optional处理等等。这里做个应用总结。
stream
构建stream
// 由值构建
Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
// 数组转stream
String[] strs = {"a", "b"};
Stream<String> stream = Arrays.stream(strs);
// generate方法也可让你按需生成一个无限流
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
// 集合直接stream()
List<String> list = Lists.newArrayList("a", "b", "c", null);
List<String> collect = list.stream().filter(Objects::nonNull).collect(Collectors.toList());
中间操作和终端操作
操作 | 类型 | 返回类型 |
---|---|---|
filter | 中间 | Stream |
distinct | 中间 | Stream |
skip | 中间 | Stream |
limit | 中间 | Stream |
map | 中间 | Stream |
flatMap | 中间 | Stream |
sorted | 中间 | Stream |
anyMatch | 终端 | boolean |
noneMatch | 终端 | boolean |
allMatch | 终端 | boolean |
findAny | 终端 | Optional |
findFirst | 终端 | Optional |
forEach | 终端 | void |
collect | 终端 | R |
reduce | 终端 | Optional |
count | 终端 | long> |
demo
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
List<Integer> collect = numbers.stream()
// 过滤
.filter(i -> i % 2 == 0)
// 去重
.distinct()
// 映射 应用每个元素
.map(i -> i * 2)
// 终端操作
.collect(Collectors.toList());
map与flatmap
- map是对流中的每个元素应用一个映射,流里的值替换成映射后的返回值
- flatmap理解这句话:flatmap方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。
一个图例很好看出他俩的区别:
集合操作
List、Map删除元素
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
map、list 删除元素
// map删除
Map<String, Integer> map = new HashMap<String, Integer>() {{
put("a", 1);
put("b", 3);
}};
map.entrySet().removeIf(e -> Objects.equals(e.getValue(), 1));
// list删除
List<Integer> list = Lists.newArrayList(1, 2, 3);
list.removeIf(e -> Objects.equals(e, 1));
合并多个map
static void test1() {
Map<String, Integer> aMap = new HashMap<String, Integer>() {{
put("a", 1);
put("b", 2);
put("c", 3);
}};
Map<String, Integer> bMap = new HashMap<String, Integer>() {{
put("a", 2);
put("b", 3);
put("d", 4);
}};
// 也要注意map的value有无null值,有则使用Optional方式
Map<String, Integer> result = Stream.of(aMap, bMap)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
Math::min));
}
聚合
分组 groupingBy
List<Person> personList = Lists.newArrayList(
new Person(11,"a"),
new Person(12,"a"),
new Person(99,"ab"));
// 按name分组 统计数量
Map<String, Long> collect = personList.stream().collect(Collectors.groupingBy(Person::getName, Collectors.counting()));
collect.forEach((k,v)->{
System.out.println(k + " "+v);
});
// ab 1
// a 2
// 按name分组,对各name下某属性求和
Map<String, Integer> collect1 = personList.stream().collect(Collectors.groupingBy(Person::getName,
Collectors.summingInt(Person::getAge)));
collect1.forEach((k,v)->{
System.out.println(k + " "+v);
});
// ab 99
// a 23
求和
List<Person> personList = Lists.newArrayList(
new Person(11,"a"),
new Person(12,"a"),
new Person(99,"ab"));
Integer a = personList.stream().collect(Collectors.summingInt(Person::getAge));
System.out.println(a);
// 或者
Integer b = personList.stream().mapToInt(Person::getAge).sum();
System.out.println(b);
Optional
创建
// 空Optional
Optional<Person> opt = Optional.empty();
Optional<Person> opt = Optional.of(person);
Optional<Person> opt = Optional.ofNullable(person);
Optional 替代null校验
一个对象,其属性包含另一个对象,如此下来。校验null时代码并不优雅,如下:
if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String isocode = country.getIsocode();
if (isocode != null) {
isocode = isocode.toUpperCase();
}
}
}
}
使用Optional方式:
String result = Optional.ofNullable(user)
.map(u -> u.getAddress())
.map(a -> a.getCountry())
.map(c -> c.getIsocode())
.orElse("default");
Optional 常见语法支持
- get()
- orElse(T other)
- orElseGet(Supplier<? extends T> other)
- orElseThrow(Supplier<? extends X> exceptionSupplier)
- ifPresent(Consumer<? super T>)
toMap 注意点
@Data
@AllArgsConstructor
static class Person {
transient Integer age;
transient String name;
}
toMap 注意NPE的情况
Optional.ofNullable()规避
static void test() {
Person p1 = new Person(1, null);
Person p2 = new Person(2, "x");
// NPE
Map<Integer, String> collect = Lists.newArrayList(p1, p2).stream()
.collect(Collectors.toMap(Person::getAge, Person::getName));
}
/**
* Collectors.toMap中 merge操作会校验value 不为null
*/
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V oldValue = get(key);
V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value);
if(newValue == null) {
remove(key);
} else {
put(key, newValue);
}
return newValue;
}
/**
* 这里使用 Optional.ofNullable(e.getName()).orElse("") 方式可避免。
*/
Map<Integer, String> collect1 = Lists.newArrayList(p1, p2).stream()
.collect(Collectors.toMap(Person::getAge,
e -> Optional.ofNullable(e.getName()).orElse("")));
toMap 注意Duplicate key情况
合并map中有相同key,需要带上 mergeFunction参数
static void test() {
Person p1 = new Person(1, null);
Person p2 = new Person(2, "x");
Person p3 = new Person(2, "y");
// mergeFunction参数避免重复key问题
Map<Integer, String> collect = Lists.newArrayList(p1, p2, p3).stream()
.collect(Collectors.toMap(Person::getAge,
e -> Optional.ofNullable(e.getName()).orElse(""), (v1, v2) -> v2));
}
常见函数式接口类
见package java.util.function。
- Function
带一个入参、出参
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
- Consumer
有入参,无出参
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
- Supplier
有出参,无入参
@FunctionalInterface
public interface Supplier<T> {
T get();
}
- Predicate
有入参,出参为boolean
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
- 带两个参数Bi开头的常用函数式接口
- BiConsumer
- BiFunction
- BiPredicate
- 等
比较器
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
比较时,一定要注意null的情况:
null友好的排序例子
@Data
@AllArgsConstructor
static class Student {
transient int age;
transient String name;
}
public static void test() {
Student ab = new Student(12, "z");
// 这里比较器中的key 是null
Student ab1 = new Student(5, null);
Student ab2 = new Student(9, "x");
// 这里整个对象是null
Student ab3 = null;
List<Student> list = Lists.newArrayList(ab, ab1, ab2, ab3);
System.out.println(list);
// 第一个nullsLast是控制Student是null的,第二个nullsFirst是控制name属性是null的
List<Student> afterSortList = list.stream().sorted(
Comparator.nullsLast(
Comparator.comparing(Student::getName, Comparator.nullsFirst(Comparator.naturalOrder()))
).thenComparing(Student::getAge)).collect(Collectors.toList());
System.out.println("排序后:");
// [Student(age=5, name=null), Student(age=9, name=x), Student(age=12, name=z), null]
System.out.println(afterSortList);
}
Comparator<T> nullsFirst(Comparator<? super T> comparator)
返回一个对null友好的比较器,null值排在前,内部实现compare方法:
@Override
public int compare(T a, T b) {
if (a == null) {
return (b == null) ? 0 : (nullFirst ? -1 : 1);
} else if (b == null) {
return nullFirst ? 1: -1;
} else {
return (real == null) ? 0 : real.compare(a, b);
}
}
参考
《Java8 实战》