//1.集合中构造
Arrays.asList(1,2,3,4,5).stream()...;
//2.静态构造
Stream.of(1,2,3,4,5)...
//3.随机数流
//IntStream
new Random(100).ints();
IntStream.of(1,2,3);
IntStream.range(1,100);
//LongStream
new Random(100).longs();
//DoubleStream
new Random(100).doubles();
//4.IntStream/LongStream/DoubleStream
//也可使用Stream<Integer>、Stream<Long>、Stream<Double>构造
IntStream.of(new int[]{1,2,3});
//5.文件输入构造
//一般Stream不须要关闭,仅仅是须要关闭在IO通道上运行的流
try(final Stream<String> lines=Files.lines(Paths.get("somePath"))){
lines.forEach(System.out::println);
}
复制代码
Stream相似于一个迭代器,能够对流中每一个元素迭代处理。串行化的处理与迭代器(Itrerator)相似,可是Stream的功能远不止迭代这么简单。java
其中的中间操做、终端操做。一般小伙伴在stream处理中一顿操做后,发现IDE爆红,常常不太明白什么缘由,不少状况下是不明白中间操做与终端操做。程序员
简单来说,中间操做执行后会返回一个流,相似于builder设计模式中build()后一般会有return this这么一个操做,中间操做返回了一个处理流从而提供了链式调用语法。而终端操做就是一个收尾操做,通常返回void或者非stream结果。如咱们经常使用的toList()、toSet()、toMap()、toArray就是非stream结果,只有反作用的 foreach() 也是void。算法
map、flatmap、filter、peek、limit、skip、distinct、sorted...都是中间操做,foreach、forEachOrdered、collect、findFirst、min、max则是终端操做。编程
//下面两种等价的方式,完成将字符串转大写并排序
//1.函数式接口方式
()->stream.of("apple","banana","orange","grapes", "melon","blueberry","blackberry")
.map(String::toUpperCase)
.sorted();
//2.Lambda表达式方式
()->stream.of("apple","banana","orange","grapes", "melon","blueberry","blackberry")
.map(v->v.toUpperCase())
.sorted();
复制代码
与map相似,都是一个函数做用于stream中的每一个元素。设计模式
从函数签名能够看出:map返回值是一个对象,对象造成了一个新的Stream。而flatmap返回的是一个Stream。flatmap不会在建立一个新Stream,而是将原来元素转换为Stream。一般用于流的扁平化处理数组
//map()签名
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
//flatmap()签名
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
//flatmap返回Stream
Stream.of(1,22,33).flatMap(v->Stream.of(v*v)).collect(Collectors.toList());
//map返回对象
Stream.of(1,22,33).map(v->v*v).collect(Collectors.toList());
//flatMap的扁平化处理
List<Map<String, String>> list = new ArrayList<>();
Map<String,String> map1 = new HashMap();
map1.put("1", "one");
map1.put("2", "two");
Map<String,String> map2 = new HashMap();
map2.put("3", "three");
map2.put("4", "four");
list.add(map1);
list.add(map2);
Set<String> output= list.stream() // Stream<Map<String, String>>
.map(Map::values) // Stream<List<String>>
.flatMap(Collection::stream) // Stream<String>
.collect(Collectors.toSet()); //Set<String>
[one, two, three,four]
复制代码
peek也是对流中的每个元素进行操做,除了生成一个包含原全部元素的新Stream,还提供一个Consumer消费函数。app
与map对比可看出peek在流处理中,能够作一些输出、外部处理、反作用等无返回值。生成一个包含原Stream的全部元素的新Stream,新Stream每一个元素在被消费以前都会执行peek给定的消费函数;dom
//对每个元素进行一些反作用
List<Integer> list = new ArrayList();
List<Integer> result = Stream.of(1, 2, 3, 4)
.peek(x -> list.add(x))
.map(x -> x * 2)
.collect(Collectors.toList());
//1
//2
//3
//[1, 2, 3]
System.out.println(list);
//map()签名,返回值R
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
//peek()签名,返回值void
Stream<T> peek(Consumer<? super T> action);
复制代码
//筛选出 >0 的元素
Arrays.asList(1,2,3,4,5)
.stream()
.filter(v-> v>0)
.toArray(Integer[]::new);
//筛选出字母A开头的字符串
Stream.of("apple","banana","orange","grapes", "melon","blueberry","blackberry")
.filter(s->s.startWith("A"))
.forEach(System.out::println)
复制代码
简单stream的去重ide
//去重
Stream.of(1,2,3,3,3,2,4,5,6)
.distinct()
.collect(Collectors.toSet());
复制代码
它老是返回 Stream 的第一个元素,或者空。注意返回值是Optional。函数式编程
Optional可能含有值,也可能不含,主要是尽量避免NPE。
Optional<String> ops = Stream.of("apple","banana","orange","blueberry","blackberry")
.filter(s->s.startsWith("b"))
.findFirst();
//banana
ops.orElse("apple");
Optional<String> ops = Stream.of("apple","banana","orange","blueberry","blackberry")
.filter(s->s.startsWith("c"))
findFirst();
//apple
ops.orElse("apple");
复制代码
也许不少人常常搞不清Collector、Collection、Collections、Collectors。 1.Collection是Java集合祖先接口; 2.Collections是java.util包下的一个工具,内含有各类处理集合的静态方法。 3.java.util.stream.Stream#collect(java.util.stream.Collector)是Stream的一个函数,负责收集流。 4.java.util.stream.Collector是一个收集函数的接口,声明一个收集器功能。 5.java.util.Collectors是一收集器的工具类,内置了一系列经常使用收集器的实现,如Collectors.toList()/toSet(),做为上述第3条的collect()参数。
toList/toMap
//toList
//方式1
List<String> list = Stream.of("I","love","you","too")
.collect(ArrayList::new,ArrayList::add,ArrayList::addAll);
//方式2
List<String> list = stream.collect(Collections.toList())
//toMap
Map<Integer, Integer> collect1 = Stream.of(1, 3, 4)
.collect(Collectors.toMap(x -> x, x -> x + 1));
复制代码
数组转List
List list = new ArrayList<>(Arrays.asList("a", "b", "c"));
Integer [] myArray = { 1, 2, 3 };
List myList = Arrays.stream(myArray).collect(Collectors.toList());
//基本类型也能够实现转换(依赖boxed的装箱操做)
int [] myArray2 = { 1, 2, 3 };
List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList());
复制代码
也许collect是咱们最经常使用的一个终端操做,toList、toSet、toMap一鼓作气。可是若是看collect函数签名会发现,这个collect不简单。
//collect1
<R> R collect( Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner) //collect2 /** @param1: supplier为结果存放容器 @param2: accumulator为结果如何添加到容器的操做 @param3: combiner则为多个容器的聚合策略 */ <R,A> R collect(collector<? super T,A,R> collector);
复制代码
从函数签名中可看出其实咱们只用到了collect的第二个方法,即便用jdk提供的Collector的toList、toSet、toMap。正在由于这些操做经常使用,故jdk中直接提供了这些collector。
咱们也能够实现本身的Collector
/* T:流中要收集的对象的泛型 A:累加器的类型,累加器是在收集过程当中用于累加部分结果的对象 R:收集操做获得的对象(一般但不必定是集合)的类型。 */
public interface Collector<T,A,R> {
//结果容器
Supplier<A> supplier();
//累加器执行累加的具体实现
BiConsumer<A, T> accumulator();
//合并2个结果的容器
BinaryOperator<A> combiner();
//对结果容器应用最终转换finisher
Function<A, R> finisher();
//characteristics
Set<Characteristics> characteristics();
}
//自定义Collector
//1.创建新的结果容器supplier(),返回值必须是一个空Supplier,供数据收集过程使用
//toList返回一个空List<>,toSet、toMap相似
@Override
public Supplier<List<T>> supplier() {
return ArrayList::new;
}
//2.累加器执行累加的具体实现accumulator()
//从BiConsumer看出返回值void,接受2个参数第一个是累计值,第二个是当期处理的第n个元素
@Override
public BiConsumer<List<T>, T> accumulator() {
//能够看出是个拼接list的操做
return List::add;
}
//3.转换最终结果容器的finisher()
//流遍历完成后,有时须要对结果处理,能够借助finisher。finisher()须返回累加过程当中的最后一个调用函数,用于将累加器对象转换为集合。
//接收2个参数,第一个参数是累加值,第二个参数是返回值,返回值就是咱们最终要的东西。
@Override
public Function<List<T>, List<T>> finisher() {
//原样输出不额外处理
//其实就是Function.identity()
return (i) -> i;
}
//4.合并容器的combiner()
//Stream支持并行操做,但并行的子部分处理规约如何处理?combiner()就是指明了各个子任务如何合并。
@Override
public BinaryOperator<List<T>> combiner() {
//每一个子任务是一个List,2个子任务结果合并累加到第一个子任务上
return (list1, list2) -> {
list1.addAll(list2);
return list1;
};
}
//5.characteristics()
复制代码
字符串的toList的Collector实现,核心是理解T、A、R
public class MyCollector<String> implements Collector<String, List<String>, List<String>> {
@Override
public Supplier<List<String>> supplier() {
return ArrayList::new;
}
@Override
public BiConsumer<List<String>, String> accumulator() {
return (List<String> l, String s) -> {
l.add(s);
};
}
@Override
public BinaryOperator<List<String>> combiner() {
return (List<String> l, List<String> r) -> {
List<String> list = new ArrayList<>(l);
list.addAll(r);
return list;
};
}
@Override
public Function<List<String>, List<String>> finisher() {
return Function.identity();
}
@Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
}
}
Stream<String> apple = Stream
.of("apple","banana", "orange", "grapes", "melon", "blueberry", "blackberry");
System.out.println(apple.collect(new MyCollector<>()));
//字符串的拼接concat
String concat = Stream
.of("apple", "apple","banana", "orange", "grapes", "melon", "berry", "blary")
.collect(StringBuilder::new,
StringBuilder::append,
StringBuilder::append)
.toString();
//等价于上面,这样看起来应该更加清晰
String concat = stringStream.collect(() -> new StringBuilder(),(l, x) -> l.append(x), (r1, r2) -> r1.append(r2)).toString();
复制代码
Integer sum = integers.reduce(0,(a,b)->a+b);
或者
Integer sum = integers.reduce(0,Integer::sum);
复制代码
//原生操做
final Integer[] integers = Lists.newArrayList(1, 2, 3, 4, 5)
.stream()
.collect(() -> new Integer[]{0}, (a, x) -> a[0] += x, (a1, a2) -> a1[0] += a2[0]);
//reducing操做
final Integer collect = Lists.newArrayList(1, 2, 3, 4, 5)
.stream()
.collect(Collectors.reducing(0, Integer::sum));
//固然Stream也提供了reduce操做
final Integer collect = Lists.newArrayList(1, 2, 3, 4, 5)
.stream().reduce(0, Integer::sum)
复制代码
String concat = Stream.of("A","B","C","D")
.reduce("",String::concat);
复制代码
double minValue = Stream.of(-1.5,1.0,-3.0,-2.0)
.reduce(Double.MAX_VALUE,Double::min);
复制代码
//有起始值
int sumValue = Stream.of(1,2,3,4)
.reduce(0,Integer::sum);
//无起始值
int sumValue = Stream.of(1,2,3,4)
.reduce(Integer::sum).get();
复制代码
Random seed = new Random();
Supplier<Integer> random = seed::nextInt;
Stream.generate(random).limit(10).forEach(System.out::println);
//Another way
IntStream.generate(() -> (int) (System.nanoTime() % 100)).
limit(10).forEach(System.out::println);
复制代码
//步长为3
Stream.iterate(0, n -> n + 3)
.limit(10)
.forEach(x -> System.out.print(x + " "));
复制代码
1.collect实现方式更简单,效率也更高。 2.reduce每次须要new ArrayList是由于reduce规定第二个参数: BiFunction accumulator表达式不能改变其自身参数acc原有值,因此每次都要new ArrayList(acc),再返回新的list。
//reduce 方式
public static <T> List<T> filter(Stream<T> stream,Predicate<T> predicate){
return stream.reduce(new ArrayList<T>(),(acc,t)->{
if(predicate.test(t)){
List<T> lists = new ArrayList<T>(acc);
lists.add(t);
return lists;
}
return acc;
}, (List<T> left,List<T> right)->{
List<T> lists= new ArrayList<T>(left);
lists.addAll(right);
return lists;
}
}
//collect
public static <T> List<T> filter(Stream<T> stream, Predicate<T> predicate) {
return stream.collect(ArrayList::new, (acc, t) -> {
if (predicate.test(t))
acc.add(t);
}, ArrayList::addAll);
}
复制代码
//使用toCollection()指定规约容器的类型
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));// (3)
HashSet<String> hashSet = stream.collect(Collectors.toCollection(HashSet::new));// (4)
复制代码
//partitioningBy()
Map<Boolean, List<Student>> passingFailing = students.stream()
.collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
//groupingBy
Map<Department, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
//mapping的下游
//按照部门对员工进行分组,而且只保留员工的名字
Map<Department,List<String>> byDept=employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.mapping(Employee::getName, //下游收集器
Collectors.toList()))); //更下游收集器
复制代码