常用
对Map进行排序
- 将 Map 转换为 Stream
- 对其进行排序
- Collect and return a new
LinkedHashMap
(保持顺序)
Map result = map.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new));
函数
函数其实就是匿名内部类
public interface Checker{
boolean doCheck(String name);
}
函数的声明lamda化
没有java8之前
new Checker() {
@Override
public boolean doCheck(String name) {
return StringUtils.isBlank(name);
}
};
有了java8后,可以用lamda表达式声明,跟js一样的,不作详解
(String name)->{
return StringUtils.isBlank(name);
}
新增函数类型返回值
没有java8之前,返回Checker对象
Checker checker = new Checker() {
@Override
public boolean doCheck(String name) {
return StringUtils.isBlank(name);
}
};
有了java8后,可以用lamda表达式声明,返回函数对象
Function function = (String name)->{
return StringUtils.isBlank(name);
}
注意!Function function = (String name)-> {xx},这里的Function用于指代"函数式接口",仅是指代,因为"函数式接口"根据入参、出参的不同,分为了很多具体的对象。
新增方法调用方式::
:: 可以用于调用静态方法,构造方法,成员方法,并返回一个函数式接口,调用不同的方法会返回不同的函数式接口。
public static class Apple{
private String name;
// 构造方法
public Apple() {
}
// 有参构造方法
public Apple(String name) {
this.name = name;
}
// 无入参、有出参的构造方法
public String getName() {
return name;
}
// 有入参、无出参的构造方法
public void setName(String name) {
this.name = name;
}
// 静态方法
public static String say() {
return "welcome to wonder land";
}
}
调用构造方法
psvm() {
// 调用无参构造器
Supplier<Apple> aNew = Apple::new;
Apple apple = aNew.get();
// 调用有参构造器
Function<String,Apple> function = Apple::new;
Apple onion = function.apply("onion");
System.out.println(onion.getName());
}
注意!可见同样是::调用构造方法,调用无参构造器、有参构造器,会返回不同的函数类型。
调用成员方法
psvm() {
Apple apple = new Apple();
Consumer<String> setName = apple::setName;
setName.accept("onion");
Supplier<String> getName = apple::getName;
String name = getName.get();
System.out.println(name);
}
调用静态方法
psvm(){
Supplier<String> say = Apple::say;
String s = say.get(); // 会返回welcome to wonder land
}
缤纷多彩的函数式接口
Predicate 接受一个泛型入参,返回boolean
// 这个是没法跑的,因为该lamda表达式真正返回的是Predicate函数式接口
Function function = (String name)->{
return StringUtils.isBlank(name);
}
// 这样才是能跑的
Predicate<String> predicate = (String name)->{
return StringUtils.isBlank(name);
}
// test,直接对入参执行判断
psvm() {
Predicate<String> predicate = (String name)->{
return StringUtils.isBlank(name);
};
predicate.test("onion");// 非空,返回false
}
// and,组合判断
psvm() {
Predicate<String> a = (String name)->{
return StringUtils.isNotBlank(name);
};
Predicate<String> b = (String name)->{
return "onion".equals(name);
};
boolean b = a.and(b).test("onion"); // 非空且等于onion,返回true
}
// or,组合判断
psvm() {
Predicate<String> a = (String name)->{
return StringUtils.isBlank(name);
};
Predicate<String> b = (String name)->{
return "onion".equals(name);
};
boolean b = a.or(b).test("onion"); // 空 或 等于onion,返回true
}
// negate,取反
psvm() {
Predicate<String> a = (String name)->{
return StringUtils.isBlank(name);
};
boolean b = a.negate().test("onion"); // 非空,则返回true
}
// equal,没啥用,null跟null比,否则用equals比
psvm() {
Predicate<Object> equal = Predicate.isEqual("123");
System.out.println(equal.test("123"));
}
Supplier<
Java8 Stream
stream:将集合变成流,流过一个管道(pipeline),可以在管道中增加中间节点对流进行处理(intermediate operations),也可以在管道终点增加收集聚合操作对零散的流进行收集或聚合(terminal operation)。
intermediate operation:
- 去重(distinct)
- 排序(sorted)
- 过滤(filter)
- 将每个元素通过函数映射成另外的元素形成新流(map),特别是将元素通过函数转为int/long流(mapToInt,mapToLong,mapToDouble)
- 合并起来替换原来的流(flatMap,flatMapToInt,flatMapToLong,flatMapToDouble)
- 元素即将进入下一个节点前执行前置动作(peek)
- 取头几个被处理的元素(limit)
- 跳过前面n个元素仅保留后半部分(skip)
terminal operation:
- 简单收集(collect)
- 取最小值(min)
- 取最大值(max)
- 统计个数(count)
- 是否有元素满足(anyMatch)
- 是否所有元素满足(allMatch)
- 是否没有元素满足(noneMatch)
- 取集合第一个元素Optional(findFirst)
中间处理 intermediate operations
在管道的中间增加节点对流进行处理,处理后返回的仍然是流。
【Java8 Stream】终点处理 terminal operation
在管道的终点对流进行处理,要么把流简单收集,要么把流收集后聚合,总之处理后返回的不再是流了。
终点处理一般靠collect和Collectors配合完成
<R, A> R collect(Collector<? super T, A, R> collector)
1. collect用于将流收集后,进行聚合或统计操作。
2. java8提供了Collectors接口,配合collect操作做一些常用的聚合统计操作。
简单收集 | 简单收集成List、set |
收集成treeSet等复杂的集合 | |
收集成一个用逗号分隔的字符串 | |
收集成map,也就是常说的list转map | |
算术 | summingInt,算总数,还有其他sum方法大同小异 |
averagingInt算平均数,还有其他average方法大同小异 | |
聚合分组 | 按对象的某个属性分组
|
按某个断言分组 | |
组合分组,先按某个条件分组,对分组结果再按某个条件分组 |
【简单收集】简单收集成List、set
// 简单收集成List、set
psvm() {
ArrayList<String> arr = Lists.newArrayList("4", "1", "3");
List<String> collect = arr.stream().collect(Collectors.toList());
List<String> collect = arr.stream().collect(Collectors.toSet());
}
// 收集成treeSet等复杂的集合
psvm() {
ArrayList<String> arr = Lists.newArrayList("4", "1", "3","3");
TreeSet<String> collect = arr.stream().collect(Collectors.toCollection(TreeSet::new));
collect.stream().forEach(System.out::println); // 打印1、3、4
}
// 收集成一个用逗号分隔的字符串
psvm() {
ArrayList<String> arr = Lists.newArrayList("4", "1", "3","3");
String collect = arr.stream().collect(Collectors.joining(","));
System.out.println(collect); // 打印4,1,3,3
}
将对象列表分组,分组后每组组员为对象的属性,或其他和原对象有映射关系的对象
本来apples按颜色分组,每组组员应该是apple对象才对,现在将组员映射成apple的批次号。
List<Apple> list = ...;
Map<String, Set<Long>> schSkuMap = list.stream().collect(
Collectors.groupingBy(Apple::getColor,
Collectors.mapping(Apple::getBatchNo, Collectors.toSet())
)
);
【简单收集】收集成map
不存在重复key时,属性->属性
// 收集成map
psvm() {
ArrayList<Person> people = Lists.newArrayList(
new Person("onion", 17),
new Person("liyc", 17));
Map<String, Integer> collect = people.stream().collect(Collectors.toMap(p -> p.getName(), p -> p.getAge()));
System.out.println(JSONObject.toJSONString(collect)); // 打印 {"liyc":17,"onion":17}
}
不存在重复key时,属性->对象 或 对象->属性
psvm() {
Map<String, Person> collect = people.stream().collect(Collectors.toMap(p -> p.getName(), Function.identity()));
System.out.println(JSONObject.toJSONString(collect));
// 打印{"liyc":{"age":17,"name":"liyc"},"onion":{"age":15,"name":"onion"}}
}
psvm() {
Map<String, Person> collect = people.stream().collect(Collectors.toMap(Function.identity(), p->p.getName()));
System.out.println(JSONObject.toJSONString(collect));
// 打印{{"age":15,"name":"onion"}:"onion",{"age":17,"name":"liyc"}:"liyc"}
}
存在重复key时,去重
@AllArgsConstructor
@NoArgsConstructor
@Data
public static class House{
private String name;
private String address;
}
psvm(){
ArrayList<House> people = Lists.newArrayList(new House("onion", "a district"),
new House("onion","b district"),
new House("liyc","c district"),
new House("liyc", "e district"));
Map<String, String> collect1 = people.stream().collect(Collectors.toMap(House::getName, House::getAddress, (s, a) -> a));
System.out.println(JSONObject.toJSONString(collect1));
// 打印 {"liyc":"e district","onion":"b district"}
}
存在重复key时,合并value返回
@AllArgsConstructor
@NoArgsConstructor
@Data
public static class House{
private String name;
private String address;
}
psvm() {
Map<String, String> collect2 = people.stream().collect(Collectors.toMap(House::getName, House::getAddress, (s, a) -> s + "," + a));
System.out.println(JSONObject.toJSONString(collect2));
// 打印{"liyc":"c district,e district","onion":"a district,b district"}
}
算术类Collectors
// 算总数,还有其他sum方法大同小异
psvm(){
ArrayList<String> arr = Lists.newArrayList("4", "1", "3","3");
Integer collect = arr.stream().collect(Collectors.summingInt(Integer::valueOf));
System.out.println(collect);
}
聚合分组类Collectors
// 按某个属性分组
psvm() {
ArrayList<Person> people = Lists.newArrayList(new Person("onion", 15),
new Person("onion", 17),
new Person("liyc", 15),
new Person("liyc", 18));
Map<String, List<Person>> collect = people.stream().collect(Collectors.groupingBy(Person::getName));
System.out.println(JSONObject.toJSONString(collect));
// 打印 {"liyc":[{"age":15,"name":"liyc"},{"age":18,"name":"liyc"}],"onion":[{"age":15,"name":"onion"},{"age":17,"name":"onion"}]}
}
// 按某个断言分组
psvm() {
ArrayList<Person> people = Lists.newArrayList(new Person("onion", 15),
new Person("onion", 17),
new Person("liyc", 15),
new Person("liyc", 18));
Map<Boolean, List<Person>> collect = people.stream().collect(Collectors.partitioningBy((item) -> item.getAge() > 15));
System.out.println(JSONObject.toJSONString(collect));
// 打印{false:[{"age":15,"name":"onion"},{"age":15,"name":"liyc"}],true:[{"age":17,"name":"onion"},{"age":18,"name":"liyc"}]}
}
// 组合分组,先按某个条件分组,对其分组结果再按另外条件分组
psvm() {
// 先按name分组,把分组结果,再按是否大于15岁分组
ArrayList<Person> people = Lists.newArrayList(new Person("onion", 15),
new Person("onion", 17),
new Person("liyc", 15),
new Person("liyc", 17));
Map<String, Map<Boolean, List<Person>>> collect = people.stream().collect(Collectors.groupingBy(Person::getName, Collectors.partitioningBy(item -> item.getAge() > 15)));
System.out.println(JSONObject.toJSONString(collect));
//打印 {"liyc":{false:[{"age":15,"name":"liyc"}],true:[{"age":17,"name":"liyc"}]},"onion":{false:[{"age":15,"name":"onion"}],true:[{"age":17,"name":"onion"}]}}
// 先按名字分组,再取每个分组年龄最大的人
Map<String, Optional<Person>> collect1 = people.stream().collect(Collectors.groupingBy(Person::getName, Collectors.maxBy(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
})));
System.out.println(JSONObject.toJSONString(collect1));
// 打印{"liyc":{"age":17,"name":"liyc"},"onion":{"age":17,"name":"onion"}}
}
Optional从容面对空指针
// 返回空的 Optional 实例。
static <T> Optional<T> empty()
// 判断其他对象是否等于 Optional, null跟null比,有值则optional内部的值与值比
boolean equals(Object obj)
// 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的
Optional<T> filter(Predicate<? super <T> predicate)
// 如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional
<U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper)
// 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException
T get()
// 返回存在值的哈希码,如果值不存在返回 0
int hashCode()
// 如果值存在则使用该值调用 consumer , 否则不做任何事情。
void ifPresent(Consumer<? super T> consumer)
// 如果值存在则方法会返回true,否则返回 false。
boolean isPresent()
// 如果存在该值,提供的映射方法,如果返回非null,返回一个Optional描述结果。
<U>Optional<U> map(Function<? super T,? extends U> mapper)
// 返回一个指定非null值的Optional。
static <T> Optional<T> of(T value)
// 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。
static <T> Optional<T> ofNullable(T value)
// 如果存在该值,返回值,否则返回 other
T orElse(T other)
// 如果存在该值,返回值,否则触发 other,并返回 other 调用的结果。
T orElseGet(Supplier<? extends T> other)
// 如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常
<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)