//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()))); //更下游收集器
複製代碼