什么是Stream?
Stream 不是集合元素肚吏,它不是數(shù)據(jù)結(jié)構(gòu)并不保存數(shù)據(jù)吮蛹,它是有關(guān)算法和計(jì)算的荤崇,它更像一個(gè)高級(jí)版本的 Iterator。原始版本的 Iterator潮针,用戶只能顯式地一個(gè)一個(gè)遍歷元素并對(duì)其執(zhí)行某些操作天试;高級(jí)版本的 Stream,用戶只要給出需要對(duì)其包含的元素執(zhí)行什么操作然低,比如 “過濾掉長度大于 10 的字符串”喜每、“獲取每個(gè)字符串的首字母”等,Stream 會(huì)隱式地在內(nèi)部進(jìn)行遍歷雳攘,做出相應(yīng)的數(shù)據(jù)轉(zhuǎn)換带兜。
Stream 就如同一個(gè)迭代器(Iterator),單向吨灭,不可往復(fù)刚照,數(shù)據(jù)只能遍歷一次枪眉,遍歷過一次后即用盡了接癌,就好比流水從面前流過醋界,一去不復(fù)返琉雳。
而和迭代器又不同的是炮叶,Stream 可以并行化操作雕沉,迭代器只能命令式地拾积、串行化操作抹蚀。顧名思義拯辙,當(dāng)使用串行方式去遍歷時(shí)郭变,每個(gè) item 讀完后再讀下一個(gè) item颜价。而使用并行去遍歷時(shí),數(shù)據(jù)會(huì)被分成多個(gè)段诉濒,其中每一個(gè)都在不同的線程中處理周伦,然后將結(jié)果一起輸出
一個(gè) Stream 只可以使用一次,下面的代碼為了簡潔而重復(fù)使用了數(shù)次未荒。
構(gòu)造流的幾種常見方法
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
stream.forEach(System.out::println);
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
//數(shù)值流的構(gòu)造
IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println);
IntStream.range(1, 3).forEach(System.out::println);
IntStream.rangeClosed(1, 3).forEach(System.out::println);
//流轉(zhuǎn)換為其它數(shù)據(jù)結(jié)構(gòu)
// 1. Array
String[] strArray1 = stream.toArray(String[]::new);
// 2. Collection
List<String> list1 = stream.collect(Collectors.toList());
List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
Set set1 = stream.collect(Collectors.toSet());
Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
// 3. String
String str = stream.collect(Collectors.joining()).toString();
流的操作
List<String> myList =
Arrays.asList("a1", "a2", "b1", "c2", "c1");
myList
.stream()
.filter(s -> s.startsWith("c"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
stream包含中間(intermediate operations)和最終(terminal operation)兩種形式的操作专挪。中間操作(intermediate operations)的返回值還是一個(gè)stream,因此可以通過鏈?zhǔn)秸{(diào)用將中間操作(intermediate operations)串聯(lián)起來片排。最終操作(terminal operation)只能返回void或者一個(gè)非stream的結(jié)果 上面的filter狈蚤,map,sorted是中間操作划纽,forEach是最終操作
中間
map
map (mapToInt, flatMap 等)作用就是把 input Stream 的每一個(gè)元素脆侮,
映射成 output Stream 的另外一個(gè)元素。
List<Integer> nums = Arrays.asList(1, 2, 3, 4);
List<Integer> squareNums =
nums.stream().map(n -> n * n).collect(Collectors.toList());
這段代碼生成一個(gè)整數(shù) list 的平方數(shù) {1, 4, 9, 16}勇劣。
filter
filter作用是過濾靖避,留下滿足條件的數(shù)
Integer[] sixNums = {1, 2, 3, 4, 5, 6};
Integer[] evens =
Stream.of(sixNums).filter(n -> n%2 == 0).toArray(Integer[]::new);
經(jīng)過條件“被 2 整除”的 filter,剩下的數(shù)字為 {2, 4, 6}比默。
distinct
distinct返回由該流的不同元素組成的流
List<String> list = Arrays.asList("AA", "BB", "CC", "BB", "CC", "AA", "AA");
long l = list.stream().distinct().count();
System.out.println("No. of distinct elements:"+l);
String output = list.stream().distinct().collect(Collectors.joining(","));
System.out.println(output);
/*
No. of distinct elements:3
AA,BB,CC
*/
ps
distinct()不提供按照屬性對(duì)對(duì)象列表進(jìn)行去重的直接實(shí)現(xiàn)幻捏。
它是基于hashCode()和equals()工作的。
如果我們想要按照對(duì)象的屬性命咐,對(duì)對(duì)象列表進(jìn)行去重篡九,我們可以通過其它方法來實(shí)現(xiàn)
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Map<Object,Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
看下面的例子distinctByKey例子
package leetcode;
public class Book {
private String name;
private int price;
public Book(String name, int price) {
this.name = name;
this.price = price;
}
public Book() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
/**************************************/
package leetcode;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.*;
public class test {
public static void main(String[] args){
List<Book> list = new ArrayList<>();
{
list.add(new Book("Core Java",200));
list.add(new Book("Core Java", 300));
list.add(new Book("Learning Freemarker", 150));
list.add(new Book("Spring MVC", 200));
list.add(new Book("Hibernate", 300));
}
list.stream().filter(distinctByKey(b -> b.getName()))
.forEach(b -> System.out.println(b.getName()+ "," + b.getPrice()));
}
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Map<Object,Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
}
/*
Core Java,200
Learning Freemarker,150
Spring MVC,200
Hibernate,300
*/
sorted
sorted 對(duì) Stream 的排序通過 sorted 進(jìn)行,它比數(shù)組的排序更強(qiáng)之處在于你可以首先對(duì) Stream 進(jìn)行各類 map醋奠、filter榛臼、limit、skip 甚至 distinct 來減少元素?cái)?shù)量后窜司,再排序
package leetcode;
import java.util.*;
import java.util.stream.*;
public class test {
public static void main(String[] args){
List<String> str = new ArrayList();
for (int i = 5; i >= 1; i--) {
str.add("num"+i);
}
List<String> strList2 = str.stream().sorted((p1, p2) -> p1.compareTo(p2)).collect(Collectors.toList());
//compareTo
// 如果指定的數(shù)與參數(shù)相等返回0沛善。
// 如果指定的數(shù)小于參數(shù)返回 -1。
// 如果指定的數(shù)大于參數(shù)返回 1
System.out.println(strList2);
}
}
//[num1, num2, num3, num4, num5]
peek
peek 對(duì)每個(gè)元素執(zhí)行操作并返回一個(gè)新的 Stream
package leetcode;
import java.util.stream.*;
public class test {
public static void main(String[] args){
Stream.of("one", "two", "three", "four")
.peek(e -> System.out.println("Filtered value: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped value: " + e))
.collect(Collectors.toList());
}
}
/*
Filtered value: one
Mapped value: ONE
Filtered value: two
Mapped value: TWO
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR
*/
limit
limit 返回 Stream 的前面 n 個(gè)元素塞祈;
package leetcode;
import java.util.stream.*;
public class test {
public static void main(String[] args){
System.out.println(Stream.of("one", "two", "three", "four")
.limit(2).collect(Collectors.toList()));
}
}
/*
[one, two]
*/
skip
skip 則是扔掉前 n 個(gè)元素
package leetcode;
import java.util.stream.*;
public class test {
public static void main(String[] args){
System.out.println(Stream.of("one", "two", "three", "four")
.skip(2).collect(Collectors.toList()));
}
}
/*
[three, four]
*/
parallelStream
parallelStream其實(shí)就是一個(gè)并行執(zhí)行的流.它通過默認(rèn)的ForkJoinPool,可能提高你的多線程任務(wù)的速度.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.parallelStream()
.forEach(System.out::print);
你得到的展示順序不一定會(huì)是1金刁、2、3议薪、4尤蛮、5、6斯议、7、8捅位、9轧葛,而可能是任意的順序
//658973421
sequential
sequential()方法:轉(zhuǎn)換當(dāng)前流為串行流
并行,串行流倆者相互轉(zhuǎn)換
package leetcode;
import java.util.*;
public class test {
public static void main(String[] args){
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
numbers.parallelStream().sequential()
.forEach(System.out::print);
}
}
//123456789
unordered
對(duì)于順序流艇搀,順序的存在與否不會(huì)影響性能尿扯,只影響確定性。如果流是順序的焰雕,則在相同的源上重復(fù)執(zhí)行相同的流管道將產(chǎn)生相同的結(jié)果;
如果是非順序流衷笋,重復(fù)執(zhí)行可能會(huì)產(chǎn)生不同的結(jié)果。 對(duì)于并行流矩屁,放寬排序約束有時(shí)可以實(shí)現(xiàn)更高效的執(zhí)行辟宗。 ...
在流有序時(shí), 但用戶不特別關(guān)心該順序的情況下,使用 unordered 明確地對(duì)流進(jìn)行去除有序約束可以改善某些有狀態(tài)或終端操作的并行性能吝秕。
unordered()操作不會(huì)執(zhí)行任何操作來顯式地對(duì)流進(jìn)行排序泊脐。
它的作用是消除了流必須保持有序的約束,從而允許后續(xù)操作使用不必考慮排序的優(yōu)化烁峭。
Stream.of(5, 1, 2, 6, 3, 7, 4).unordered().forEach(System.out::print);
Stream.of(5, 1, 2, 6, 3, 7, 4).unordered().parallel().forEach(System.out::print);
5126374
3641527
最終
forEach
forEach():對(duì)流中的每個(gè)元素執(zhí)行某個(gè)操作
上面很多了
forEachOrdered
forEachOrdered()輸出的順序與元素的順序嚴(yán)格一致
//主要的區(qū)別在并行流的處理上
//輸出的順序不一定(效率更高)
Stream.of("AAA", "BBB", "CCC").parallel().forEach(s -> System.out.println("Output:" + s));
//輸出的順序與元素的順序嚴(yán)格一致
Stream.of("AAA", "BBB", "CCC").parallel().forEachOrdered(s -> System.out.println("Output:" + s));
toArray
toArray():將流轉(zhuǎn)換為數(shù)組
collect
collect():對(duì)流的匯總操作容客,比如輸出成List集合
// Integer[] arr = {1,2,3,4,5}; 和下面相等的
// List<Integer> list = new ArrayList<>(Arrays.asList(arr));
// list.forEach(System.out :: print);
List<Integer> list= Arrays.asList(1,2,3,4,5)
.stream().collect(Collectors.toList());
list.forEach(System.out :: print);
reduce
reduce()對(duì)流中的元素歸約操作,將每個(gè)元素合起來形成一個(gè)新的值
求累加和
package leetcode;
import java.util.*;
public class test {
public static void main(String[] args){
Arrays.asList(1,2,3)
.stream()
.reduce((sum,i)-> sum = sum+i)
.ifPresent(System.out::print);
//使用ifPresent()來進(jìn)行對(duì)象操作,存在則操作,否則不操作
}
}
//6
更多reduce
// 字符串連接约郁,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
// 求最小值缩挑,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
// 求和鬓梅,sumValue = 10, 無起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
// 過濾供置,字符串連接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F").
filter(x -> x.compareTo("Z") > 0).
reduce("", String::concat);
上面代碼例如第一個(gè)示例的 reduce()绽快,第一個(gè)參數(shù)(空白字符)即為起始值芥丧,第二個(gè)參數(shù)(String::concat)為 BinaryOperator。
這類有起始值的 reduce() 都返回具體的對(duì)象坊罢。而對(duì)于第四個(gè)示例沒有起始值的 reduce()娄柳,
由于可能沒有足夠的元素,返回的是 Optional(它是個(gè)啥艘绍?我也不曉得)赤拒,請(qǐng)留意這個(gè)區(qū)別。
min诱鞠,max挎挖,findFirst,findAny操作這4個(gè)函數(shù)航夺,都是返回的Optional對(duì)象
這個(gè)對(duì)象蕉朵,是用來解決空指針的問題
簡單了解 get()是取值 使用ifPresent()來進(jìn)行對(duì)象操作,存在則操作,否則不操作
https://blog.csdn.net/qq_28410283/article/details/80952768
min/max/count
min():求最小值
max():求最大值
count():求總數(shù)
package leetcode;
import java.util.*;
public class test {
public static void main(String[] args){
// 獲取流 Book上面有哦
List<Book> books = Arrays.asList(
new Book("Java編程思想", 108),
new Book("Java 8實(shí)戰(zhàn)", 79),
new Book("MongoDB權(quán)威指南(第2版)",69));
// 計(jì)算所有圖書的總價(jià)
Optional<Integer> totalPrice = books.stream().map(Book::getPrice).reduce((n, m) -> n + m);
int sum = totalPrice.get();
System.out.println(sum);
// 價(jià)格最高的圖書
Book newbook = new Book();
newbook = books.stream().max(Comparator.comparing(Book::getPrice)).get();
System.out.println(newbook.getName() + " , "+ newbook.getPrice());
// 價(jià)格最低的圖書
newbook = books.stream().min(Comparator.comparing(Book::getPrice)).get();
System.out.println(newbook.getName() + " , "+ newbook.getPrice());
// 計(jì)算總數(shù)
long count = books.stream().count();
System.out.println(count);
}
}
/*
256
Java編程思想 , 108
MongoDB權(quán)威指南(第2版) , 69
3
*/
查找和匹配
查找方法有`anyMatch()`、`allMatch()`阳掐、`noneMatch()`始衅、`findFirst()`冷蚂、`findAny()`,這些方法被用來查找或匹配某些元素是否符合給定的條件:
// 檢查流中的任意元素是否包含字符串"Java"
boolean hasMatch = Stream.of("Java", "C#", "PHP", "C++", "Python")
.anyMatch(s -> s.equals("Java"));
// 檢查流中的所有元素是否都包含字符串"#"
boolean hasAllMatch = Stream.of("Java", "C#", "PHP", "C++", "Python")
.allMatch(s -> s.contains("#"));
// 檢查流中的任意元素是否沒有以"C"開頭的字符串
boolean hasNoneMatch = Stream.of("Java", "C#", "PHP", "C++", "Python")
.noneMatch(s -> s.startsWith("C"));
// 查找元素
Optional<String> element = Stream.of("Java", "C#", "PHP", "C++", "Python")
.filter(s -> s.contains("C"))
.findAny(); // 查找任意元素
// .findFirst() // 查找第一個(gè)元素
分組
Collectors.groupingBy()
Collectors.groupingBy()類似于數(shù)據(jù)庫中GROUP BY分組的特性
package leetcode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class test {
public static void main(String[] args){
List<String> items = Arrays.asList(
"apple", "apple",
"orange", "orange", "orange",
"blueberry",
"peach", "peach", "peach", "peach"
);
// 分組汛闸,計(jì)數(shù)
Map<String, Long> result = items.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(result);
}
}
//{orange=3, apple=2, blueberry=1, peach=4}
Function.identity()返回一個(gè)輸出跟輸入一樣的Lambda表達(dá)式對(duì)象蝙茶,等價(jià)于形如t -> t形式的Lambda表達(dá)式。
identity() 方法JDK源碼如下:
static <T> Function<T, T> identity() {
return t -> t;
}
對(duì)一個(gè)對(duì)象進(jìn)行分組計(jì)數(shù)
/********************Item.java**********************/
package leetcode;
import java.math.BigDecimal;
public class Item {
private String name;
private int qty;
private BigDecimal price; //大整數(shù)的處理類
public Item() {
}
public Item(String name, int qty, BigDecimal price) {
this.name = name;
this.qty = qty;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getQty() {
return qty;
}
public void setQty(int qty) {
this.qty = qty;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
@Override
public String toString() {
return "Item{" +
"name='" + name + '\'' +
", qty=" + qty +
", price=" + price +
'}';
}
}
/*********************test.java*********************/
package leetcode;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
public class test {
public static void main(String[] args){
List<Item> items = Arrays.asList(
new Item("apple", 10, new BigDecimal(23.5)),
new Item("apple", 20, new BigDecimal(32.5)),
new Item("orange", 30, new BigDecimal(13.9)),
new Item("orange", 20, new BigDecimal(33.5)),
new Item("orange", 10, new BigDecimal(63.5)),
new Item("orange", 50, new BigDecimal(41.5)),
new Item("peach", 20, new BigDecimal(26.5)),
new Item("peach", 30, new BigDecimal(42.5)),
new Item("peach", 40, new BigDecimal(24.5)),
new Item("peach", 10, new BigDecimal(12.5))
);
// 分組诸老,計(jì)數(shù)
Map<String, Long> counting = items.stream()
.collect(Collectors.groupingBy(Item::getName, Collectors.counting()));
System.out.println(counting);
// 分組隆夯,計(jì)數(shù),數(shù)量
Map<String, Integer> sum = items.stream()
.collect(Collectors.groupingBy(Item::getName, Collectors.summingInt(Item::getQty)));
// Collectors.summingInt返回流中整數(shù)屬性求和
System.out.println(sum);
}
}
/*
{orange=4, apple=2, peach=4}
{orange=110, apple=30, peach=100}
*/
最后附上Java8 Collectors 收集器靜態(tài)方法
主要方法:
參考原文:
https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/#N101E8