一:什么是 Stream?
Stream(流)是一個(gè)來(lái)自數(shù)據(jù)源的元素隊(duì)列并支持聚合操作
Java中的Stream并不會(huì)存儲(chǔ)元素,而是按需計(jì)算。 數(shù)據(jù)源 流的來(lái)源。 可以是集合,數(shù)組,I/O channel, 產(chǎn)生器generator 等。
聚合操作 類(lèi)似SQL語(yǔ)句一樣的操作, 比如filter, map, reduce, find, match, sorted等。 和以前的Collection操作不同,Stream操作還有兩個(gè)基礎(chǔ)的特征如下:
- Pipelining: 中間操作都會(huì)返回流對(duì)象本身。 這樣多個(gè)操作可以串聯(lián)成一個(gè)管道, 如同流式風(fēng)格。 這樣做可以對(duì)操作進(jìn)行優(yōu)化, 比如延遲執(zhí)行和短路。
- 內(nèi)部迭代: 以前對(duì)集合遍歷都是通過(guò)Iterator或者For-Each的方式, 顯式的在集合外部進(jìn)行迭代, 這叫>做外部迭代。 Stream提供了內(nèi)部迭代的方式, 通過(guò)訪(fǎng)問(wèn)者模式(Visitor)實(shí)現(xiàn)。
二:Stream API 使用
1:使用Stream步驟:
(1)先產(chǎn)生一個(gè)流(Stream)一個(gè)數(shù)據(jù)源,獲取一個(gè)流。
(2)中間鏈?zhǔn)讲僮?一個(gè)中間的操作鏈,對(duì)數(shù)據(jù)源的數(shù)據(jù)進(jìn)行處理。
(3)產(chǎn)生一個(gè)新流:一個(gè)終止操作,執(zhí)行中間操作,產(chǎn)生結(jié)果。
注意:Stream操作是延遲執(zhí)行,他們會(huì)等需要結(jié)果的時(shí)候才會(huì)執(zhí)行。
總結(jié):
- 中間操作常用方法有:篩選:filter 映射:map 排序:sorted提取與組合 收集:collect。
- 終止操作:遍歷:foreach 匹配:find、match 規(guī)約:reduce 聚合:max、min、count。
2:創(chuàng)建Stream的方法的4種方式
【1】Collection接口中的方法:
default Stream<E> stream() 獲取串行流
default Stream<E> parallelStream() 獲取并行流
案例:
//方式1:Collection接口的方法
Collection collection = new ArrayList();
Stream stream = collection.stream();
Stream stream1 = collection.parallelStream();
//方式2:通過(guò)Arrays中的Stream方法 數(shù)組
IntStream stream2 = Arrays.stream(new int[]{1, 2, 3, 4, 5});
//方式3:Stream中的of方法
Stream<String> stream3 = Stream.of("111", "222", "333");
//方法4:Stream中的方法 創(chuàng)建無(wú)限流 (結(jié)果是無(wú)線(xiàn)個(gè))
Stream<Integer> iterate = Stream.iterate(2, (x) -> x + 2);
3:中間操作
1:篩選與切片
① Stream filter(Predicate<?super T> predicate)返回由與此給定謂詞匹配的此流的元素組成的流。 --->接收Lambda,從流中排除某些元素。
//1:創(chuàng)建Stream;
Stream<Student> stream = list.stream();
//2:filter方法(找到年齡大于等于18歲的學(xué)生)
Stream<Student> studentStream = stream.filter((student) -> student.getAge() >= 18);
//3:終止操作;如果沒(méi)有終止操作的話(huà),上面的第二步中間操作不執(zhí)行
studentStream.forEach(System.out::println);
/**
* 注意:如果值執(zhí)行1,2操作的話(huà),不會(huì)有任何結(jié)果。
* 驗(yàn)證出Steam操作是延遲的,只有進(jìn)行了終止操作,才會(huì)執(zhí)行中間操作!這就是所謂的延遲加載
*/
②Stream limit(Long maxSize) 返回由該流的元素組成的流,截?cái)嚅L(zhǎng)度不能超過(guò)maxSize. 只有找到maxSize個(gè)滿(mǎn)足條件的即可。 ---->截?cái)嗔?,使其元素不超過(guò)給定的數(shù)量。
public void limitTest02() {
//Limit方法 短路(效率增高),只要找到了2個(gè)滿(mǎn)足條件的,后面的迭代操作就不在執(zhí)行了!
list.stream().filter(x -> {
System.out.println("正在過(guò)濾!!");
return x.getAge() > 18;
}).limit(2).forEach(System.out::println);
}
③Stream skip(Long n) 在丟掉流的第一個(gè)n元素后,返回由該流的n元素組成的流,如果此流包含少于n元素,那么將返回一個(gè)空流。 ---->跳過(guò)元素,返回一個(gè)扔掉了前n個(gè)元素的流。 如果流中的元素不足n個(gè),則返回一個(gè)空流,與limit(n)互補(bǔ)。
public void skipTest03() {
//skip 方法跳過(guò)前2個(gè)滿(mǎn)足條件的 留下后面滿(mǎn)足條件的結(jié)果??!
list.stream().filter(x -> {
System.out.println("正在過(guò)濾后面滿(mǎn)足條件的結(jié)果");
return x.getAge() > 18;
}).skip(2).forEach(System.out::println);
}
④Stream distinct()
注意: 自定義的類(lèi)在去重的過(guò)程中必須重新hashCode和equals方法,因?yàn)閐istinct實(shí)現(xiàn)的時(shí)候底層去找這兩個(gè)方法。
public void distinctTest04() {
//distinct 去重操作!
list.stream().distinct().forEach(System.out::println);
}
⑤ map映射:
如果需要將流中的元素映射到另一個(gè)流中,可以使用map方法。方法簽名: Stream map(Function<? super T, ? extends R> mapper); 該接口需要一個(gè)Function函數(shù)式接口參數(shù),可以將當(dāng)前流中的T類(lèi)型數(shù)據(jù)轉(zhuǎn)換為另一種R類(lèi)型的流。 Stream流中的map方法基本使用的代碼如:
@Test
public void testMap() {
Stream<String> original = Stream.of("11", "22", "33");
Stream<Integer> result = original.map(Integer::parseInt);
result.forEach(s -> System.out.println(s + 10));
}
//這段代碼中,map方法的參數(shù)通過(guò)方法引用,將字符串類(lèi)型轉(zhuǎn)換成為了int類(lèi)型(并自動(dòng)裝箱為Integer類(lèi)對(duì)象)。
⑥ 排序 (兩種方式)
(1)Stream sorted()返回此流元素組成的流,根據(jù)自然順序排序。底層按照內(nèi)部比較器進(jìn)行排序,實(shí)現(xiàn)Comparable接口中的compareTo方法。
(2)Stream sorted(Comparator<?super T>comparator) 返回由此元素組成的流,根據(jù)挺的Comparator進(jìn)行順序排序。指定順序。 指定排序策略:底層按照外部比較器進(jìn)行排序 Comparator接口一定要重新Compare方法。
基本使用
Stream流中的sorted方法基本使用的代碼如:
@Test
public void testSorted() {
// sorted(): 根據(jù)元素的自然順序排序
// sorted(Comparator<? super T> comparator): 根據(jù)比較器指定的規(guī)則排序
Stream.of(33, 22, 11, 55)
.sorted()
.sorted((o1, o2) -> o2 - o1)
.forEach(System.out::println);
}
這段代碼中,sorted方法根據(jù)元素的自然順序排序,也可以指定比較器排序。
4:終止操作
①查找(find)和匹配(match)
如果需要找到某些數(shù)據(jù),可以使用find相關(guān)方法。方法簽名:
- Optional findFirst();
- Optional findAny();
Stream流中的find相關(guān)方法使用代碼:
@Test
public void testFind() {
Optional<Integer> first = Stream.of(5, 3, 6, 1).findFirst();
System.out.println("first = " + first.get());
Optional<Integer> any = Stream.of(5, 3, 6, 1).findAny();
System.out.println("any = " + any.get());
}
Stream流的match方法
如果需要判斷數(shù)據(jù)是否匹配指定的條件,可以使用Match相關(guān)方法。方法簽名:
- boolean allMatch(Predicate<? super T> predicate);
- boolean anyMatch(Predicate<? super T> predicate);
- boolean noneMatch(Predicate<? super T> predicate);
基本使用 Stream流中的Match相關(guān)方法使用代碼如:
@Test
public void testMatch() {
boolean b = Stream.of(5, 3, 6, 1)
// .allMatch(e -> e > 0); // allMatch: 元素是否全部滿(mǎn)足條件
// .anyMatch(e -> e > 5); // anyMatch: 元素是否任意有一個(gè)滿(mǎn)足條件
.noneMatch(e -> e < 0); // noneMatch: 元素是否全部不滿(mǎn)足條件
System.out.println("b = " + b);
}
②:遍歷 foreach
//forEach 用來(lái)遍歷流中的數(shù)據(jù)
@Test
public void test02() {
//案例1、2下面兩種寫(xiě)法等同
list.stream().map((x)->x.getName()).forEach(System.out::println);
list.stream().map(Student::getName).forEach(System.out::println);
}
③Stream流的max、min
List<String> list13 = Arrays.asList("zhangsan","lisi","wangwu","xuwujing");
int maxLines = list13.stream().mapToInt(String::length).max().getAsInt();
int minLines = list13.stream().mapToInt(String::length).min().getAsInt();
System.out.println("最長(zhǎng)字符的長(zhǎng)度:" + maxLines+",最短字符的長(zhǎng)度:"+minLines);
//最長(zhǎng)字符的長(zhǎng)度:8,最短字符的長(zhǎng)度:4
④Stream流的count
// Stream流提供count方法來(lái)統(tǒng)計(jì)其中的元素個(gè)數(shù):long count();
//該方法返回一個(gè)long值代表元素個(gè)數(shù)。基本使用:
@Test
public void testCount() {
List<String> strList = new ArrayList<>();
Collections.addAll(strList, "張無(wú)忌", "周芷若", "趙敏", "小昭", "楊不悔);
System.out.println(strList.stream().count());
}
⑤ 分組:groupingBy;
當(dāng)我們使用Stream流處理數(shù)據(jù)后,可以根據(jù)某個(gè)屬性將數(shù)據(jù)分組:
// 案例:
@Test
public void testGroup() {
Stream<Student> studentStream = Stream.of(
new Student("趙麗穎", 52, 95),
new Student("楊穎", 56, 88),
new Student("迪麗熱巴", 56, 55),
new Student("柳巖", 52, 33));
// Map<Integer, List<Student>> map = studentStream.collect(Collectors.groupingBy(Student::getAge));
// 將分?jǐn)?shù)大于60的分為一組,小于60分成另一組
Map<String, List<Student>> map = studentStream.collect(Collectors.groupingBy((s) -> {
if (s.getSocre() > 60) {
return "及格";
} else {
return "不及格";
}
}));
map.forEach((k, v) -> {
System.out.println(k + "::" + v);
});
}
效果:
不及格::[Student{name='迪麗熱巴', age=56, socre=55}, Student{name='柳巖', age=52, socre=33}]
及格::[Student{name='趙麗穎', age=52, socre=95}, Student{name='楊穎', age=56, socre=88}]
⑥拼接:joining
Collectors.joining會(huì)根據(jù)指定的連接符,將所有元素連接成一個(gè)字符串。
// 拼接
@Test
public void testJoining() {
Stream<Student> studentStream = Stream.of(
new Student("趙麗穎", 52, 95),
new Student("楊穎", 56, 88),
new Student("迪麗熱巴", 56, 99),
new Student("柳巖", 52, 77));
String collect = studentStream
.map(Student::getName)
.collect(Collectors.joining(">_<", "^_^", "^v^"));
System.out.println(collect);
}
效果:
^_^趙麗穎>_<楊穎>_<迪麗熱巴>_<柳巖^v^
⑦聚合:toList,toSet,toMap;
Stream流提供collect方法,其參數(shù)需要一個(gè)java.util.stream.Collector<T,A, R>接口對(duì)象來(lái)指定收集到哪種集合中。
- public static Collector<T, ?, List> toList():轉(zhuǎn)換為L(zhǎng)ist集合。
- public static Collector<T, ?, Set> toSet():轉(zhuǎn)換為Set集合。
- public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper):轉(zhuǎn)換為Map集合。
下面是這兩個(gè)方法的基本使用代碼:
// 將流中數(shù)據(jù)收集到集合中
@Test
public void testStreamToCollection() {
Stream<String> stream = Stream.of("aa", "bb", "cc");
// List<String> strList = stream.collect(Collectors.toList());
// Set<String> strSet = stream.collect(Collectors.toSet());
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));
HashSet<String> hashSet = stream.collect(Collectors.toCollection(HashSet::new));
}
toMap
@Test
public void testCollectToMap(){
//案例1
List<Integer> list = Arrays.asList(1, 2, 3);
Map<String, String> collect1 = list.stream().map(i -> i).collect(Collectors.toMap(key -> "key" + key, value -> "value:" + value));
//實(shí)體list轉(zhuǎn)化map id作為主鍵,對(duì)象作為value
List<User> userList =new ArrayList<User>();
UserTask userTask = new UserTask();
userTask.setId(1);
userTask.setName("測(cè)試");
userList.add(userTask);
Map<Integer,UserTask> taskMap = userList.stream().collect(Collectors.toMap(UserTask::getId, entity -> entity));
System.out.println(collect1.toString());
System.out.println(taskMap.toString());
}
以上就是 Java 8 新特性之Stream流的介紹以及具體API的使用方法的全部?jī)?nèi)容,想要了解更多關(guān)于 JAVA 8 其他新特性的資料,請(qǐng)關(guān)注W3Cschool其它相關(guān)文章!也希望大家能夠多多地支持我們!