JAVA8新特性--集合流操作Stream


Stream類全路徑為:java.util.stream.Stream
對Stream的描述,引用其他文章中覺得比較好的介紹:

Java 8 中的 Stream 是對集合(Collection)對象功能的增強,它專注於對集合對象進行各種非常便利、高效的聚合操作(aggregate operation),或者大批量數據操作 (bulk data operation)。Stream API 借助於同樣新出現的 Lambda 表達式,極大的提高編程效率和程序可讀性。

即Stream的原理:

這種風格將要處理的元素集合看作一種流, 流在管道中傳輸, 並且可以在管道的節點上進行處理, 比如篩選, 排序,聚合等。元素流在管道中經過中間操作(intermediate operation)的處理,最后由最終操作(terminal operation)得到前面處理的結果。

集合有兩種方式生成流:
- stream() − 為集合創建串行流。
- parallelStream() − 為集合創建並行流。

並發模式(parallelStream)能夠充分利用多核處理器的優勢,使用 fork/join 並行方式來拆分任務和加速處理過程。java中也提供了多種並行處理的方式,看到一片文章對各幾種並行的方式性能進行了測試,可以參考下:java中幾種並行方式的性能分析

Stream的的中間操作(intermediate)和最終操作(terminal)都包含哪些方法可以從類結構中看到:
這里寫圖片描述
這里寫圖片描述
上面截圖基本包含了Strram的所有方法。

  • 中間操作(intermediate)主要有以下方法(此類型的方法返回的都是Stream對象):
    map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered

  • 終端操作(terminal)主要有以下方法:
    forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator

此處按照類結構(截圖)中的方法的順序(以下例子中使用了Lambda表達式及方法引用,不了解的請戳Lambda介紹方法引用),進行介紹:

  • filter:通過設置條件來過濾元素。
List<String> list = Arrays.asList("aaa","ddd","bbb","ccc","a2a","d2d","b2b","c2c","a3a","d3d","b3b","c3c");

list.stream()
        .filter((s)->s.contains("a"))
        .forEach(s -> System.out.println(s));
   
   
  
  
          

以上代碼使用filter方法過濾出只包含”a”的元素,然后通過forEach將滿足條件的元素遍歷出來。輸出如下:

aaa
a2a
a3a

  • map:就是將對應的元素使用給定方法進行轉換。
list.stream()
        .filter((s)->s.contains("a"))
        .map((s)-> s + "---map")
        .forEach(s -> System.out.println(s));

   
   
  
  
          

在filter的基礎上,給每個元素后面添加字符串”—map”,輸出如下:

aaa—map
a2a—map
a3a—map

  • mapToInt:和map方法進行一樣的操作,但是轉換函數必須返回int類型
  • mapToLong:和map方法進行一樣的操作,但是轉換函數必須返回long類型
  • mapToDouble:和map方法進行一樣的操作,但是轉換函數必須返回double類型
    此處以mapToInt進行演示:
list.stream()
        .filter((s)->s.contains("a"))
        .mapToInt((s)-> s.hashCode())
        .forEach(s -> System.out.println(s));
   
   
  
  
          

在filter的基礎上,將每個元素轉換為其hashCode。輸出為:

96321
94864
94895

  • flatMap:如果流的元素為數組或者Collection,flatMap就是將每個Object[]元素或Collection<Object>元素都轉換為Object元素,如下:

Stream<String[]> 轉換為 Stream<String>
Stream<Set> 轉換為 Stream<String>
Stream<List> 轉換為 Stream<String>
Stream<List> 轉換為 Stream<Object>

看下例子:

List<String[]> setList =  new ArrayList<>();
setList.add(new String[]{"aa","bb"});
setList.add(new String[]{"cc","dd"});
setList.add(new String[]{"ee","ff"});
//使用map方法
setList.stream()
        .map(s->Arrays.stream(s))
        .forEach(s-> System.out.println("map==" + s));
//使用flatMap方法
setList.stream()
        .flatMap(s->Arrays.stream(s))
        .forEach(s-> System.out.println("flatMap==" + s));
   
   
  
  
          

輸出如下:

map==java.util.stream.ReferencePipeline&Head@50040f0c
map==java.util.stream.ReferencePipeline&Head@2dda6444
map==java.util.stream.ReferencePipeline&Head@5e9f23b4
flatMap==aa
flatMap==bb
flatMap==cc
flatMap==dd
flatMap==ee
flatMap==ff

可以看出map就是將數組流直接返回,flatMap是將數組流中的每個元素都返回flatMapToIntflatMapToLongflatMapToDouble類似,只不過返回的是對應的類型的流,此處不做演示。

  • distinct:將集合中的元素去重。
List<String> disList = Arrays.asList("aaa","ddd","bbb","ddd","aaa");
disList.stream()
        .distinct()
        .forEach(s-> System.out.println(s));
   
   
  
  
          

輸出如下:

aaa
ddd
bbb

  • sorted:將集合中的元素排序。
List<Integer> integerList = Arrays.asList(1,2,3,4);
integerList.stream()
        .sorted()
        .forEach(s-> System.out.println(s));
   
   
  
  
          

輸出如下:

1
2
3
4

可以按照自定義排序:

integerList.stream()
        .sorted((s1,s2)->s2.compareTo(s1))
        .forEach(s-> System.out.println(s));
   
   
  
  
          

輸出如下:

4
3
2
1

  • peek:生成一個包含原Stream的所有元素的新Stream,同時會提供一個消費函數即引用的方法A,當Stream每個元素被消費的時候都會先執行新Stream給定的方法A。peek是中間操作,如果pee后沒有最終操作,則peek不會執行。
integerList.stream()
        .peek(s-> System.out.println("peek = "+s));
   
   
  
  
          

此時沒有輸出,代碼改為:

integerList.stream()
        .peek(s-> System.out.println("peek = "+s))
        .forEach(s-> System.out.println("forEach = "+s));
   
   
  
  
          

輸出如下:

peek = 1
forEach = 1
peek = 2
forEach = 2
peek = 3
forEach = 3
peek = 4
forEach = 4

  • limit:返回Stream的前n個元素。
integerList.stream()
        .limit(1)
        .forEach(s-> System.out.println(s));
   
   
  
  
          

輸出為:

1

  • skip:刪除Stream的前n個元素。
integerList.stream()
        .skip(1)
        .forEach(s-> System.out.println(s));
   
   
  
  
          

輸出如下:

2
3
4

  • forEach:遍歷Stream中的每個元素,前面每個例子都有使用,此處不再演示。
  • forEachOrdered:遍歷Stream中的每個元素。
    區別
    在串行流(stream)中沒有區別,在並行流(parallelStream)中如果數據源是有序集合,forEachOrdered輸出順序與數據源中順序一致,forEach則是亂序。

看下使用forEach:

integerList.stream()
        .forEach(s-> System.out.println(s));
   
   
  
  
          

輸出(多次測試,每次結果都不一樣):

3
1
4
2

再看使用forEachOrdered:

integerList.parallelStream()
        .forEachOrdered(s-> System.out.println(s));
   
   
  
  
          

輸出(測試多次,每次都是這個結果,與integerList中的元素順序一致):

2
1
3
4

  • toArray:將流轉換為Object[]或者指定類型的數組。
Object[] array = integerList.stream().toArray();
String[] strArr = integerList.stream().toArray(String[]::new);
   
   
  
  
          
  • reduce:將集合中的每個元素聚合成一條數據。有三種情況:

  • reduce(BinaryOperator accumulator):此處需要一個參數,返回Optional對象:
Optional<Integer> reduce = integerList.stream().reduce((a, b) -> a + b);
   
   
  
  
          
  
  
 
 
         
  • 1
  • reduce(T identity, BinaryOperator accumulator):此處需要兩個參數,第一個參數為起始值,第二個參數為引用的方法。從起始值開始,每個元素執行一次引用的方法(方法引用的中的兩個參數:第一個參數為上個元素執行方法引用的結果,第二個參數為當前元素)。
int integer = integerList.stream().reduce(5,(a, b) -> a + b);
System.out.println(integer);
   
   
  
  
          

輸出為:

15

此例中使用起始值為5,對集合中每個元素求和,可以理解為:5+2+1+3+4=15。

  • reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner):此處需要三個參數。此方法用在並發流(parallelStream)中,啟動多個子線程使用accumulator進行並行計算,最終使用combiner對子線程結果進行合並,返回identity類型的數據,看到有篇文章對這個解釋比較清楚:java8中3個參數的reduce方法怎么理解?
  • collect:將流轉換成集合或聚合元素。有兩種情況。接受一個參數和接受三個參數(三個參數在並發流parallelStream中使用),此處介紹一個參數的情況,單個參數接受的參數類型為Collector,Collectors 類實現了很多歸約操作,詳見:java8之collector
List<Integer> collects = integerList.stream()
        .filter(a -> a > 1)
        .collect(Collectors.toList());
System.out.println(collects);
   
   
  
  
          

此處統計集合中大於1的元素並最終返回list。輸出如下:

[2, 3, 4]

  • min:獲取集合中最小值。
Integer min = integerList.stream()
        .filter(a -> a > 1)
        .min((Integer a, Integer b) -> a.compareTo(b))
        .get();
System.out.println(min);
   
   
  
  
          

輸出為:

2

  • max:獲取集合中最大值。
Integer max = integerList.stream()
        .filter(a -> a > 1)
        .max((Integer a, Integer b) -> a.compareTo(b))
        .get();
System.out.println(max);
   
   
  
  
          

輸出為:

4

  • count:獲取集合中元素個數
long count = integerList.stream()
        .filter(a -> a > 1)
        .count();
System.out.println(count);
   
   
  
  
          

輸出為:

3

  • anyMatch: Stream 中任意一個元素符合傳入的 predicate,返回 true
  • allMatch:Stream 中全部元素符合傳入的 predicate,返回 true
  • noneMatch:Stream 中沒有一個元素符合傳入的 predicate,返回 true
boolean b = integerList.stream()
        .anyMatch(s -> s > 0);
boolean b1 = integerList.stream()
        .allMatch(s -> s > 0);
boolean b2 = integerList.stream()
        .noneMatch(s -> s > 0);
System.out.println("anyMatch = " + b);
System.out.println("allMatch = " + b1);
System.out.println("noneMatch = " + b2);
   
   
  
  
          

輸出:

anyMatch = true
allMatch = true
noneMatch = false

  • findFirst:如果數據源是有序集合,返回Stream 中第一個元素的Optional對象,如果是無序集合,則返回Stream 中任意一個元素的Optional對象。
Integer first = integerList.stream()
        .findFirst()
        .get();
System.out.println(first);
   
   
  
  
          

輸出為:

2

  • findAny:返回Stream 中任意一個元素的Optional對象。
Integer any = integerList.stream()
        .findAny()
        .get();
System.out.println(any);
   
   
  
  
          

輸出為:

2

Stream還有幾個靜態方法,返回都是Stream對象。
靜態方法如下:builder(返回Builder對象)、empty、of、iterate、generate、concat。
builder:返回一個Builder對象,Builder對象在調用build()返回Stream對象。
empty:返回一個空的有序的Stream對象。
of:返回包含單個元素的有序的Stream對象。
iterate:返回一個無限元素的有序的Stream對象。需要兩個參數,第一個參數為初始值,第二個參數為要引用的方法,然后會通過遞歸循環調用引用的方法。

Stream.iterate(2,s->s+s)
        .limit(10)
        .forEach(s-> System.out.println(s));
   
   
  
  
          

輸出為:

2
4
8
16
32
64
128
256
512
1024

generate:返回一個無限元素的無序的的Stream對象。需要一個參數,參數為引用的方法,然后會通過循環調用引用的方法來生成元素,常用於生成常量Stream和隨機元素Stream。
concat:將兩個Stream連接成一個Stream。需要兩個Stream作為參數,如果兩個Stream都是有序的並且無論參數Stream是否是並行Stream,得到的都是有序的Stream。輸出元素順序為先輸出第一個Stream的元素,然后輸出第二個Stream的元素。當結果Stream關閉時候,兩個參數Stream同時關閉。

Stream.concat(integerList.stream(),disList.stream())
        .forEach(s-> System.out.println(s));
   
   
  
  
          

輸出為:

2
1
3
4
11
aaa
ddd
33
bbb
ddd
aaa

原文地址:https://blog.csdn.net/bluuusea/article/details/79967039


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM