用lls建設(shè)一個(gè)網(wǎng)站百度地圖導(dǎo)航手機(jī)版免費(fèi)下載
JAVA8的新特性——Stream
在這個(gè)深夜寫下這篇筆記,窗外很安靜,耳機(jī)里是《季節(jié)更替》,我感觸還不是很多,當(dāng)我選擇封面圖片的時(shí)候才發(fā)現(xiàn)我們已經(jīng)漸漸遠(yuǎn)去,我們都已經(jīng)奔赴生活,都在拼命想著去換一個(gè)活法,六月就要真的結(jié)束這段關(guān)系,好懷念校園的那段日子,我們不用去思考以后,在這個(gè)象牙塔里幻想一份感情,互相玩笑,走出來了,就真的遠(yuǎn)去!愿我們所得皆所愿,不被生活磨平了棱角,聰哥,腿哥,偉哥,呂子,帆子,還有此刻讀到文章的你,加油,我們一起向前沖!
——2023年5月15日凌晨 IT小輝同學(xué)
Java 8中引入的Stream是一種全新的數(shù)據(jù)處理方式,它使用簡(jiǎn)單而高效的方法對(duì)集合數(shù)據(jù)進(jìn)行操作。下面對(duì)Stream進(jìn)行詳細(xì)介紹
什么是 Stream(引用菜鳥教程)
Stream(流)是一個(gè)來自數(shù)據(jù)源的元素隊(duì)列并支持聚合操作
- 元素是特定類型的對(duì)象,形成一個(gè)隊(duì)列。 Java中的Stream并不會(huì)存儲(chǔ)元素,而是按需計(jì)算。
- 數(shù)據(jù)源 流的來源。 可以是集合,數(shù)組,I/O channel, 產(chǎn)生器generator 等。
- 聚合操作 類似SQL語句一樣的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作還有兩個(gè)基礎(chǔ)的特征:
- Pipelining: 中間操作都會(huì)返回流對(duì)象本身。 這樣多個(gè)操作可以串聯(lián)成一個(gè)管道, 如同流式風(fēng)格(fluent style)。 這樣做可以對(duì)操作進(jìn)行優(yōu)化, 比如延遲執(zhí)行(laziness)和短路( short-circuiting)。
- 內(nèi)部迭代: 以前對(duì)集合遍歷都是通過Iterator或者For-Each的方式, 顯式的在集合外部進(jìn)行迭代, 這叫做外部迭代。 Stream提供了內(nèi)部迭代的方式, 通過訪問者模式(Visitor)實(shí)現(xiàn)
Stream的用法
Stream是Java 8中針對(duì)集合數(shù)據(jù)的一種新的操作方式,使用了類似于SQL語句的流式操作方式。Stream由三部分構(gòu)成:源、零個(gè)或多個(gè)中間操作、終止操作。
源是指Stream要處理的原始數(shù)據(jù)源,可以是Collection、Array、I/O資源等;
中間操作用來對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)化、過濾、排序等操作,其中每一個(gè)中間操作都會(huì)返回一個(gè)新的Stream對(duì)象;
終止操作用來獲取Stream處理結(jié)果。
Stream的特點(diǎn)
(1)流式操作:Stream提供了一種流式的操作方式,方便數(shù)據(jù)處理和轉(zhuǎn)換;
(2)惰性求值:Stream使用惰性求值方式進(jìn)行計(jì)算,只有在被終止操作時(shí)才會(huì)進(jìn)行計(jì)算;
(3)并行處理:Stream支持并行處理,提高了處理大數(shù)據(jù)集的效率。
Stream的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
(1)簡(jiǎn)潔:使用Stream可以讓代碼更加簡(jiǎn)潔易懂;
(2)高效:Stream支持惰性求值和并發(fā)處理,提高了處理效率;
(3)靈活:Stream提供了多種操作方式,可以適應(yīng)不同的數(shù)據(jù)處理需求。
缺點(diǎn):
(1)學(xué)習(xí)成本:Stream需要掌握一些新的操作方式,需要一定的學(xué)習(xí)成本;
(2)線程安全:并行處理時(shí)需要注意線程安全問題。
Stream的使用場(chǎng)景
Stream適用于處理大數(shù)據(jù)集和需要頻繁進(jìn)行轉(zhuǎn)換、過濾、排序等操作的場(chǎng)景。例如,對(duì)于需要篩選和排序的訂單列表或者用戶列表等數(shù)據(jù)集合,Stream可以極大地提高處理效率和代碼簡(jiǎn)潔度。
Stream的發(fā)展趨勢(shì)
隨著Java生態(tài)系統(tǒng)的不斷發(fā)展,Stream也在不斷完善和優(yōu)化中。未來Stream可能會(huì)增加更多的功能和特性,同時(shí)也會(huì)進(jìn)一步提升處理效率和優(yōu)化用戶體驗(yàn)。
總之,Stream是Java 8中非常重要的一個(gè)新特性,它為集合數(shù)據(jù)的處理提供了一種全新的方式,具有很多優(yōu)點(diǎn)和適用場(chǎng)景。因此,熟練掌握Stream的各種用法和特點(diǎn)對(duì)Java開發(fā)者來說是非常重要的。
Java生成Stream
Java中可以通過多種方式來生成Stream,包括:
- 從集合中生成Stream:
可以使用Collection接口提供的stream()方法或parallelStream()方法來生成Stream對(duì)象。例如:
List<String> list = Arrays.asList("apple", "banana", "orange");
Stream<String> stream = list.stream();
- 從數(shù)組中生成Stream:
可以使用Arrays類提供的stream()方法或parallelStream()方法來生成Stream對(duì)象。例如:
int[] arr = new int[]{1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(arr);
- 使用Stream.of()方法生成Stream:
可以使用Stream類提供的of()方法來生成Stream對(duì)象。例如:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
- 使用Stream.iterate()方法生成Stream:
可以使用Stream類提供的iterate()方法來生成Stream對(duì)象。該方法需要傳入一個(gè)初始值和一個(gè)函數(shù)接口,用來生成無限長(zhǎng)度的Stream。例如:
Stream<Integer> stream = Stream.iterate(1, n -> n + 1);
- 使用Stream.generate()方法生成Stream:
可以使用Stream類提供的generate()方法來生成Stream對(duì)象。該方法需要傳入一個(gè)函數(shù)接口,用來生成無限長(zhǎng)度的Stream。例如:
Stream<Double> stream = Stream.generate(Math::random);
總之,Java中可以通過多種方式來生成Stream對(duì)象,可以根據(jù)不同的需求選擇不同的方式來生成Stream。
Stream中的filter過濾
filter()
方法可以用于過濾出滿足特定條件的元素。
具體來說,filter()
方法會(huì)接收一個(gè)函數(shù)式接口Predicate<T>
,該接口只有一個(gè)方法test(T t)
,該方法接受一個(gè)參數(shù)t
,返回一個(gè)布爾值。當(dāng)test()
方法的返回值為true
時(shí),表示當(dāng)前元素應(yīng)該被保留;當(dāng)返回值為false
時(shí),表示當(dāng)前元素應(yīng)該被過濾掉。
/*** @Description 過濾掉apple這個(gè)字符串* @Author IT小輝同學(xué)* @Date 2023/05/14*/@Testpublic void test1() {List<String> list = Arrays.asList("apple", "banana", "orange","cherry","Hami");Stream<String> stream = list.stream();//過濾掉apple這個(gè)字符串List<String> stringList =stream.filter(s -> !s.equals("apple")).collect(Collectors.toList());System.out.println("過濾掉apple這個(gè)字符串:"+stringList);}/*** @Description 過濾掉含字母a的字符串* @Author IT小輝同學(xué)* @Date 2023/05/14*/@Testpublic void test2() {List<String> list = Arrays.asList("apple", "banana", "orange","cherry","Hami");Stream<String> stream = list.stream();//過濾掉含字母a的字符串List<String> stringList1=stream.filter(s -> !s.contains("a")).collect(Collectors.toList());System.out.println("過濾掉含字母a的字符串:"+stringList1);}/*** @Description 過濾掉含字母a和i的字符串* @Author IT小輝同學(xué)* @Date 2023/05/14*/@Testpublic void test3() {List<String> list = Arrays.asList("apple", "banana", "orange","cherry","Hami");Stream<String> stream = list.stream();//過濾掉含字母a和i的字符串List<String> stringList1=stream.filter(s -> !s.contains("a")&&!s.contains("i")).collect(Collectors.toList());System.out.println("過濾掉含字母a和i的字符串:"+stringList1);}
注意:同一個(gè)Stream不可進(jìn)行多次操作,否則報(bào)錯(cuò)
錯(cuò)誤示例
List<String> list = Arrays.asList("apple", "banana", "orange","cherry","Hami");Stream<String> stream = list.stream();//過濾掉apple這個(gè)字符串List<String> stringList =stream.filter(s -> !s.equals("apple")).collect(Collectors.toList());System.out.println("過濾掉apple這個(gè)字符串:"+stringList);//過濾掉含字母a和i的字符串List<String> stringList1=stream.filter(s -> !s.contains("a")&&!s.contains("i")).collect(Collectors.toList());System.out.println("過濾掉含字母a和i的字符串:"+stringList1);
報(bào)錯(cuò)情況
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closedat java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)at java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94)at java.util.stream.ReferencePipeline$StatelessOp.<init>(ReferencePipeline.java:618)at java.util.stream.ReferencePipeline$2.<init>(ReferencePipeline.java:163)at java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:162)at com.demo.example.StreamDemo.main(StreamDemo.java:20)什么原因
詳細(xì)解釋
這個(gè)異常通常是因?yàn)閷?duì)一個(gè)已經(jīng)被操作或關(guān)閉的Stream進(jìn)行了再次操作或關(guān)閉,導(dǎo)致Stream狀態(tài)不正確,從而拋出IllegalStateException異常。解決這個(gè)問題的方法通常有以下幾種:
- 避免在同一個(gè)Stream上進(jìn)行多個(gè)終止操作:
即使多個(gè)終止操作被串聯(lián)在一起,也只會(huì)執(zhí)行最后一個(gè)終止操作。因此,在一個(gè)Stream對(duì)象上執(zhí)行多個(gè)終止操作會(huì)導(dǎo)致Stream被關(guān)閉,從而引發(fā)IllegalStateException異常。如果需要對(duì)同一個(gè)Stream進(jìn)行多個(gè)操作,可以將其保存到一個(gè)中間變量中,并對(duì)該中間變量進(jìn)行操作。例如:
Stream<String> stream = list.stream();
stream = stream.filter(s -> s.length() > 5);
stream.forEach(System.out::println);
- 使用新建的Stream對(duì)象進(jìn)行操作:
對(duì)于Stream對(duì)象的所有操作都會(huì)返回一個(gè)新的Stream對(duì)象,因此可以通過使用新建的Stream對(duì)象來避免對(duì)已經(jīng)被操作或關(guān)閉的Stream進(jìn)行操作。例如:
Stream<String> stream1 = list.stream();
Stream<String> stream2 = stream1.filter(s -> s.length() > 5);
stream2.forEach(System.out::println);
- 改用Stream.peek()方法:
peek()方法是一種類似于forEach()方法但不會(huì)關(guān)閉Stream的中間操作??梢酝ㄟ^使用peek()方法來進(jìn)行調(diào)試和測(cè)試操作。例如:
Stream<String> stream = list.stream();
stream = stream.filter(s -> s.length() > 5).peek(System.out::println);
stream.forEach(System.out::println);
總之,如果出現(xiàn)了IllegalStateException異常,通常是因?yàn)閷?duì)一個(gè)已經(jīng)被操作或關(guān)閉的Stream進(jìn)行了再次操作或關(guān)閉??梢酝ㄟ^避免在同一個(gè)Stream對(duì)象上進(jìn)行多個(gè)終止操作、使用新建的Stream對(duì)象進(jìn)行操作或改用Stream.peek()方法來解決這個(gè)問題。
Stream中的forEach遍歷
forEach()方法是一種用于遍歷Stream元素并對(duì)每個(gè)元素進(jìn)行指定操作的終止操作方法。
forEach()方法接收一個(gè)Lambda表達(dá)式作為參數(shù),該表達(dá)式會(huì)被應(yīng)用到Stream中的每個(gè)元素上,用來執(zhí)行指定的操作。該方法不返回任何值,只是將Lambda表達(dá)式應(yīng)用到Stream中的每個(gè)元素上,從而實(shí)現(xiàn)對(duì)Stream的遍歷操作。
/*** @Description 簡(jiǎn)單遍歷* @Author IT小輝同學(xué)* @Date 2023/05/15*/@Testpublic void test4(){List<String> list = Arrays.asList("apple", "banana", "orange","cherry","Hami");Stream<String> stream = list.stream();stream.forEach(System.out::println);}/*** @Description 遍歷去除含有字母b的字符串* @Author IT小輝同學(xué)* @Date 2023/05/15*/@Testpublic void test5(){List<String> list = Arrays.asList("apple", "banana", "orange","cherry","Hami");Stream<String> stream = list.stream();stream.filter(s -> !s.contains("b")).forEach(System.out::println);}/*** @Description 遍歷去除含有字母b和i的字符串并且添加到arrayList* @Author IT小輝同學(xué)* @Date 2023/05/15*/@Testpublic void test6(){List<String> list = Arrays.asList("apple", "banana", "orange","cherry","Hami");ArrayList<String> arrayList=new ArrayList<>();Stream<String> stream = list.stream();stream.filter(s -> !s.contains("b")).forEach(arrayList::add);System.out.println(arrayList);}
Stream中的map映射
map操作可以將Stream中的每個(gè)元素映射到另一個(gè)元素上,并返回一個(gè)新的Stream。
map操作的語法如下:
Stream<T> map(Function<? super T, ? extends U> mapper)
其中,T是Stream中的元素類型,U是映射后的元素類型。mapper是一個(gè)函數(shù)式接口,它接受一個(gè)元素作為輸入,并返回一個(gè)新的元素作為輸出。
map操作的作用是將Stream中的每個(gè)元素映射到另一個(gè)元素上,并返回一個(gè)新的Stream。這個(gè)操作非常靈活,可以用于很多不同的場(chǎng)景。
/*** @Description 使用 map 輸出了元素對(duì)應(yīng)的平方數(shù)* @Author IT小輝同學(xué)* @Date 2023/05/15*/@Testpublic void test7() {List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);// 獲取對(duì)應(yīng)的平方數(shù)List<Integer> squaresList = numbers.stream().map(i -> i * i).collect(Collectors.toList());System.out.println(squaresList);}/*** @Description 使用 map 輸出了元素對(duì)應(yīng)的平方數(shù)(去重)* @Author IT小輝同學(xué)* @Date 2023/05/15*/@Testpublic void test8() {List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);// 獲取對(duì)應(yīng)的平方數(shù)List<Integer> squaresList = numbers.stream().map(i -> i * i).distinct().collect(Collectors.toList());System.out.println(squaresList);}/*** @Description 使用 map 輸出了元素對(duì)應(yīng)的平方數(shù)(去重并且排序)* @Author IT小輝同學(xué)* @Date 2023/05/15*/@Testpublic void test9() {List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);// 獲取對(duì)應(yīng)的平方數(shù)List<Integer> squaresList = numbers.stream().map(i -> i * i).distinct().sorted().collect(Collectors.toList());System.out.println(squaresList);}/*** @Description 使用 map 輸出了元素對(duì)應(yīng)的平方數(shù)(去重并且排序,限制個(gè)數(shù))* @Author IT小輝同學(xué)* @Date 2023/05/15*/@Testpublic void test10() {List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);// 獲取對(duì)應(yīng)的平方數(shù)List<Integer> squaresList = numbers.stream().map(i -> i * i).distinct().sorted().limit(3).collect(Collectors.toList());System.out.println(squaresList);}
Stream中的parallel并行程序
parallel操作可以將Stream中的每個(gè)元素并行處理,并返回一個(gè)新的Stream。
parallel操作的語法如下:
Stream<T> parallel()
其中,T是Stream中的元素類型。
parallel操作的作用是將Stream中的每個(gè)元素并行處理,并返回一個(gè)新的Stream。這個(gè)操作可以用于處理大量數(shù)據(jù),提高處理效率。
需要注意的是,parallel操作會(huì)創(chuàng)建一個(gè)新的線程來處理每個(gè)元素,這可能會(huì)導(dǎo)致性能開銷。因此,在使用parallel操作時(shí),需要根據(jù)具體的情況來評(píng)估其性能開銷,并進(jìn)行適當(dāng)?shù)膬?yōu)化。
/*** @Description 獲取空字符串的數(shù)量* @Author IT小輝同學(xué)* @Date 2023/05/15*/@Testpublic void test11() {List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");// 獲取空字符串的數(shù)量long count = strings.parallelStream().filter(string -> string.isEmpty()).count();System.out.println(count);}/*** @Description 去除空字符串轉(zhuǎn)換為數(shù)組* @Author IT小輝同學(xué)* @Date 2023/05/15*/@Testpublic void test12() {List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");// 獲取空字符串的數(shù)量List<String> stringList = strings.parallelStream().filter(string ->!string.isEmpty()).collect(Collectors.toList());System.out.println(stringList);}
Stream中的Collectors聚合操作
Stream中的Collectors聚合操作是一種用于對(duì)流數(shù)據(jù)進(jìn)行處理的方法,它可以將流中的元素按照指定的規(guī)則進(jìn)行分組、過濾、映射等操作,最終生成一個(gè)新的流。
以下是一些常見的聚合操作:
- toList():將流中的元素收集到一個(gè)List中。
List<Integer> list = stream.collect(Collectors.toList());
- toSet():將流中的元素收集到一個(gè)Set中,去重。
Set<Integer> set = stream.collect(Collectors.toSet());
- toMap():將流中的元素按照指定的鍵值對(duì)進(jìn)行分組,并返回一個(gè)Map對(duì)象。
Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), Function.identity()));
- maxBy():按照指定的屬性或方法對(duì)流中的元素進(jìn)行比較,返回最大值。
Optional<Integer> max = stream.max(Comparator.comparingInt(i -> i));
- minBy():按照指定的屬性或方法對(duì)流中的元素進(jìn)行比較,返回最小值。
Optional<Integer> min = stream.min(Comparator.comparingInt(i -> i));
- sum():對(duì)流中的數(shù)字類型元素進(jìn)行求和。
long sum = stream.mapToLong(i -> i).sum();
- count():統(tǒng)計(jì)流中元素的數(shù)量。
long count = stream.count();
- average():計(jì)算流中數(shù)字類型元素的平均值。
double average = stream.mapToDouble(i -> i).average().orElse(0d);
- filter():根據(jù)指定的條件過濾流中的元素。
stream.filter(i -> i > 10).forEach(System.out::println); // 輸出大于 10 的元素
- distinct():去重操作,返回不同的元素組成的流。
List<Integer> distinctList = stream.distinct().collect(Collectors.toList()); // 去重后生成 List 集合
使用Collectors可以簡(jiǎn)化代碼,提高開發(fā)效率,同時(shí)也可以實(shí)現(xiàn)更加靈活的數(shù)據(jù)處理方式。
Stream中的統(tǒng)計(jì)函數(shù)
另外,一些產(chǎn)生統(tǒng)計(jì)結(jié)果的收集器也非常有用。它們主要用于int、double、long等基本類型上,它們可以用來產(chǎn)生類似如下的統(tǒng)計(jì)結(jié)果。
好的,以下是Stream中的統(tǒng)計(jì)函數(shù)介紹:
- count():統(tǒng)計(jì)流中元素的數(shù)量。
long count = stream.count();
- min():返回流中最小的元素。
Optional<T> min = stream.min(Comparator.comparing(i -> i));
- max():返回流中最大的元素。
Optional<T> max = stream.max(Comparator.comparing(i -> i));
- sum():對(duì)流中的元素進(jìn)行求和。
long sum = stream.mapToLong(i -> i).sum();
- average():計(jì)算流中元素的平均值。
double average = stream.mapToDouble(i -> i).average().orElse(0d);
- distinct():返回去重后的元素列表。
List<T> distinctList = stream.distinct().collect(Collectors.toList()); // 去重后生成 List 集合
- anyMatch():判斷流中是否存在至少一個(gè)滿足條件的元素。
boolean anyMatch = stream.anyMatch(i -> i > 10); // 判斷是否存在大于 10 的元素
- allMatch():判斷流中的所有元素是否都滿足條件。
boolean allMatch = stream.allMatch(i -> i > 10); // 判斷所有元素是否都大于 10
- noneMatch():判斷流中是否不存在滿足條件的元素。
boolean noneMatch = stream.noneMatch(i -> i < 10); // 判斷是否不存在小于 10 的元素
一個(gè)小例子結(jié)束學(xué)習(xí)之旅
數(shù)字平方排序(倒敘)輸出
字符串轉(zhuǎn) map 輸出
public static void main(String[] args) {List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);// 獲取對(duì)應(yīng)的平方數(shù)// List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());List<Integer> squaresList = numbers.stream().map(i -> i * i).sorted((x, y) -> y - x).collect(Collectors.toList());// squaresList.forEach(System.out::println);squaresList.forEach(num -> {num++;System.out.println(num);});List<String> strList = Arrays.asList("a", "ba", "bb", "abc", "cbb", "bba", "cab");Map<Integer, String> strMap = new HashMap<Integer, String>();strMap = strList.stream().collect( Collectors.toMap( str -> strList.indexOf(str), str -> str ) );strMap.forEach((key, value) -> {System.out.println(key+"::"+value);});}
sorted()方法的第二個(gè)參數(shù)是一個(gè)比較器(Comparator),用于指定排序規(guī)則。比較器的邏輯接受兩個(gè)參數(shù),分別代表要比較的兩個(gè)元素,返回一個(gè)負(fù)數(shù)、零或正數(shù),表示第一個(gè)參數(shù)小于、等于或大于第二個(gè)參數(shù)。
在sorted((x, y) -> y - x)中,括號(hào)內(nèi)的第一個(gè)參數(shù)x和第二個(gè)參數(shù)y分別代表要比較的兩個(gè)元素。比較器的邏輯是按照從每個(gè)元素到自身的降序差值進(jìn)行比較,即用第二個(gè)元素減去第一個(gè)元素,得到的結(jié)果就是它們之間的大小關(guān)系。如果結(jié)果為負(fù)數(shù),則說明第一個(gè)元素小于第二個(gè)元素;如果結(jié)果為0,則說明它們相等;如果結(jié)果為正數(shù),則說明第一個(gè)元素大于第二個(gè)元素。
因此,sorted((x, y) -> y - x)實(shí)際上定義了一個(gè)從每個(gè)元素到自身的降序差值的比較器,用于對(duì)列表進(jìn)行排序操作。
前輩的文章推薦(如果感覺對(duì)于個(gè)人這篇文章已經(jīng)透徹,可以看看前輩的文章,本人學(xué)習(xí)之后,方知自我之淺薄)