详情看:
英文:https://github.com/shekhargulati/java8-the-missing-tutorial
比较好的博文:
https://www.cnblogs.com/aspirant/p/10132730.html.
https://blog.csdn.net/u013291394/article/details/52662761.
交集与并集
/*并集*/
List<AClass> unionResult = Stream.of(aClassList1, aClassList2)
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
assertEquals(unionResult.size(), 5);
System.out.println(unionResult);
/*交集*/
/*[AClass(id=1, name=zhuoli1, description=haha1)]*/
List<AClass> intersectResult = aClassList1.stream()
.filter(aClassList2::contains)
.collect(Collectors.toList());
System.out.println(intersectResult);
//交集
Collection<String> intersection = CollectionUtils.intersection(listA, listB);
flatMap
- flatMap ——合并多个list
/*合并多个list*/
List<AClass> aClassListResult = map.values().stream()
.flatMap(listContainer -> listContainer.getLst().stream())
.collect(Collectors.toList());
/*注意跟并集的区别*/
assertEquals(aClassListResult.size(), 6);
- flatMap ——对给定单词列表 [“Hello”,“World”],你想返回列表[“H”,“e”,“l”,“o”,“W”,“r”,“d”]
String[] words = new String[]{"Hello","World"};
List<String> a = Arrays.stream(words)
.map(word -> word.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(toList());
a.forEach(System.out::print);
- flatMap的复杂操作,List<Data1>和List<Data2>根据Id进行连接,将连接结果输出为一个List<OutputData>:
@Data
@AllArgsConstructor
public class Data1 {
private int id;
private String name;
private int amount;
}
@Data
@AllArgsConstructor
public class Data2 {
private int id;
private String name;
private String type;
}
@Data
@AllArgsConstructor
public class OutputData {
private int id;
private String name;
private String type;
private int amount;
}
@Test
public void intersectByKeyTest(){
List<Data2> listOfData2 = new ArrayList<Data2>();
listOfData2.add(new Data2(10501, "JOE" , "Type1"));
List<Data1> listOfData1 = new ArrayList<Data1>();
listOfData1.add(new Data1(10501, "JOE" ,3000000));
List<OutputData> result = listOfData1.stream()
.flatMap(x -> listOfData2.stream()
.filter(y -> x.getId() == y.getId())
.map(y -> new OutputData(y.getId(), x.getName(), y.getType(), x.getAmount())))
.collect(Collectors.toList());
System.out.println(result);
/*difference by key*/
List<Data1> data1IntersectResult = listOfData1.stream()
.filter(data1 -> listOfData2.stream()
.map(Data2::getId)
.collect(Collectors.toList())
.contains(data1.getId()))
.collect(Collectors.toList());
System.out.println(data1IntersectResult);
- 使用flatMap统计——字数统计样例
public static void wordCount(Path path) throws IOException {
Map<String, Long> wordCount = Files.lines(path)
.parallel()
.flatMap(line -> Arrays.stream(line.trim().split("\\s")))
.map(word -> word.replaceAll("[^a-zA-Z]", "").toLowerCase().trim())
.filter(word -> word.length() > 0)
.map(word -> new SimpleEntry<>(word, 1))
.collect(groupingBy(SimpleEntry::getKey, counting()));
wordCount.forEach((k, v) -> System.out.println(String.format("%s ==>> %d", k, v)));
}
- faltMapToInt、flatMapToDouble、flatMapToLong
List<List<String>> listOfLists = Arrays.asList(
Arrays.asList("1", "2"),
Arrays.asList("5", "6"),
Arrays.asList("3", "4")
);
IntStream intStream =
listOfLists.stream()
.flatMapToInt(childList ->
childList.stream()
.mapToInt(Integer::new));
int sum = intStream.peek(System.out::println).sum();
System.out.println("sum: " + sum);
Collectors
下表展示 Collectors 类的静态工厂方法。
工厂方法 返回类型 作用
- toList List 把流中所有项目收集到一个 List
- toSet Set 把流中所有项目收集到一个 Set,删除重复项
- toCollection Collection 把流中所有项目收集到给定的供应源创建的集合
- menuStream.collect(toCollection(), ArrayList::new)
- counting Long 计算流中元素的个数
- sumInt Integer 对流中项目的一个整数属性求和
- averagingInt Double 计算流中项目 Integer 属性的平均值
- summarizingInt IntSummaryStatistics 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值
- joining String 连接对流中每个项目调用 toString 方法所生成的字符串collect(joining(", "))
- maxBy Optional 一个包裹了流中按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty()
- minBy Optional 一个包裹了流中按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty()
- reducing 归约操作产生的类型 从一个作为累加器的初始值开始,利用 BinaryOperator 与流 中的元素逐个结合,从而将流归约为单个值累加int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum));
- collectingAndThen 转换函数返回的类型 包裹另一个收集器,对其结果应用转换函数int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size))
- groupingBy Map<K, List> 根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键
- partitioningBy Map<Boolean,List> 根据对流中每个项目应用谓词的结果来对项目进行分区
工作中遇到的tomap方式
// if (!CollectionUtils.isEmpty(dpsCharVars)) {
// 用 (k1,k2)->k1 来设置,如果有重复的key,则保留key1,舍弃key2
// Map<String, CharVar> charVarMap = dpsCharVars.parallelStream()
// .collect(Collectors.toMap(CharVar::getEnName, var -> var, (k1, k2) -> k1));
// }
- Collectors toMap,当value为null时空指针报错 的处理方式如下:
params.stream().collect(LinkedHashMap::new, (m, v) -> m.put(v.getParam(), v.getParamValue()), LinkedHashMap::putAll);
罪魁祸首就是HashMap的merge方法了,它的第一行就是这个:
if (value == null) throw new NullPointerException();
为什么会调merge方法呢,toMap方法调的
public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier) { BiConsumer<M, T> accumulator = (map, element) -> map.merge(keyMapper.apply(element), valueMapper.apply(element), mergeFunction); return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID); }
那么怎么解决呢? 既然时merge方法造成的,那就不调merge方法。 我们用自己定义的accumulator,用Stream的另一个collect方法
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
这个方法上面的注释写了一段这个, 前两个参数干什么用的就很清楚了,第三个参数时并行计算用来组合结果的,所以用HashMap的putAll就好了
R result = supplier.get(); for (T element : this stream) accumulator.accept(result, element); return result;
所以解决办法的代码大概就是这样的
params.stream().collect(LinkedHashMap::new, (m, v) -> m.put(v.getParam(), v.getParamValue()), LinkedHashMap::putAll);
据说这个问题java9就修复了,所以也可以尝试升级jdk
Consumer
public class MyTest {
public static void printValur(String str){
System.out.println("print value : "+str);
}
public static void main(String[] args) {
List<String> al = Arrays.asList("a", "b", "c", "d");
al.forEach(AcceptMethod::printValur);
//下面的方法和上面等价的
Consumer<String> methodParam = AcceptMethod::printValur; //方法参数
al.forEach(x -> methodParam.accept(x));//方法执行accept
}
}
上面的所有方法执行玩的结果都是如下:
print value : a
print value : b
print value : c
print value : d
在JDK8中,接口Iterable 8中默认实现了forEach方法,调用了 JDK8中增加的接口Consumer内的accept方法,执行传入的方法参数。
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
另外补充一下,JDK8改动的,在接口里面可以有默认实现,就是在接口前加上default,实现这个接口的函数对于默认实现的方法可以不用再实现了。类似的还有static方法。现在这种接口除了上面提到的,还有BiConsumer,BiFunction,BinaryOperation等,在java.util.function包下的接口,大多数都有,后缀为Supplier的接口没有和别的少数接口。
最近新看到的lambda表达式,对我的collect操作起到了很大的促进作用:
需求起源于:如何将一个List<A> 转换成Map<String,List<B>> ???
characterRangeList.stream().collect(Collectors.groupingBy(new Function<A, Integer>() {
@Override
public Integer apply(A aa) {
return aa.getVarId();
}
}, HashMap::new, Collectors.mapping(new Function<A, B>() {
@Override
public B apply(A aa) {
return null;
}
}, Collectors.toList())))
.entrySet().stream().map(new Function<Map.Entry<String, List<Integer>>, B>() {
@Override
public B apply(Map.Entry<String, List<Integer>> stringListEntry) {
return new B(stringListEntry.getKey(), stringListEntry.getValue());
}
}).collect(Collectors.toList());
System.out.println(JSON.toJSONString(listB));
Function
public static void main(String[] args) {
Function<Integer, Integer> times2 = i -> i*2;
Function<Integer, Integer> squared = i -> i*i;
System.out.println(times2.apply(4));
System.out.println(squared.apply(4));
//32 先4×4然后16×2,先执行apply(4),在times2的apply(16),先执行参数,再执行调用者。
System.out.println(times2.compose(squared).apply(4));
//64 先4×2,然后8×8,先执行times2的函数,在执行squared的函数。
System.out.println(times2.andThen(squared).apply(4));
//16
System.out.println(Function.identity().compose(squared).apply(4));
}
computeIfAbsent
在使用 Map 时推荐一个不错的函数 computeIfAbsent:
- 只有在当前 Map 中 key 对应的值不存在或为 null 时, 才调用 mappingFunction
- 并在 mappingFunction 执行结果非 null 时, 将结果跟 key 关联.
/**
* mappingFunction 为空时 将抛出空指针异常
*/
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
Map<String, List<String>> map = new HashMap<>();
List<String> list;
// 一般这样写
list = map.get("list-1");
if (list == null) {
list = new LinkedList<>();
map.put("list-1", list);
}
list.add("one");
// 使用 computeIfAbsent 可以这样写
list = map.computeIfAbsent("list-1", k -> new ArrayList<>());
list.add("one");
orElse() 和 orElseGet()的区别
Optional.ofNullable(record)
.map(columns -> ...)
.orElse(Maps.newHashMap());
Optional.ofNullable(record)
.map(columns -> ...)
.orElseGet(()->Maps.newHashMap());
累积 —— reduce
Arrays.asList(1, 2, 3, 4).stream().reduce((a, b) -> a * b).get();//24
遍历移除
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.removeIf(s -> s%2==0); // 过滤掉模2等于0的数
list.forEach(s -> System.out.println(s)); // 输出 1 3
List<String> strings = new ArrayList<>();
strings.add("ab");
strings.add("ac");
strings.add("bc");
strings.add("cd");
Predicate<String> predicate = (s) -> s.startsWith("a"); // 这里单独定义了过滤器
strings.removeIf(predicate); // 过滤掉以"a"开头的元素
strings.forEach(s -> System.out.println(s)); // 输出 bc cd