一文詳解Java中Stream流的使用
簡(jiǎn)介
說(shuō)明
本文用實(shí)例介紹stream的使用。
JDK8新增了Stream(流操作) 處理集合的數(shù)據(jù),可執(zhí)行查找、過(guò)濾和映射數(shù)據(jù)等操作。
使用Stream API 對(duì)集合數(shù)據(jù)進(jìn)行操作,就類似于使用 SQL 執(zhí)行的數(shù)據(jù)庫(kù)查詢??梢允褂?Stream API 來(lái)并行執(zhí)行操作。
簡(jiǎn)而言之,Stream API 提供了一種高效且易于使用的處理數(shù)據(jù)的方式。
特點(diǎn)
不是數(shù)據(jù)結(jié)構(gòu),不會(huì)保存數(shù)據(jù)。
大部分不修改原來(lái)的數(shù)據(jù)源,它會(huì)將操作后的數(shù)據(jù)保存到另外一個(gè)對(duì)象中。
peek方法可以修改流中元素
惰性求值,流在中間處理過(guò)程中,只對(duì)操作進(jìn)行記錄,不會(huì)立即執(zhí)行,需等到執(zhí)行終止操作的時(shí)候才會(huì)進(jìn)行實(shí)際的計(jì)算。
Stream操作步驟
創(chuàng)建Stream=> 轉(zhuǎn)換Stream(中間操作)=> 產(chǎn)生結(jié)果(終止操作)
注意:這只是一般操作。實(shí)際編程時(shí),創(chuàng)建必須有,而中間操作與終止操作是可選的。
操作分類

無(wú)狀態(tài):指元素的處理不受之前元素的影響;
有狀態(tài):指該操作只有拿到所有元素之后才能繼續(xù)下去。
非短路操作:指必須處理所有元素才能得到最終結(jié)果;
短路操作:指遇到某些符合條件的元素就可以得到最終結(jié)果,如 A || B,只要A為true,則無(wú)需判斷B的結(jié)果。
本文的公共代碼
?class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
操作1:創(chuàng)建流
Collection下的 stream() 和 parallelStream() 方法
List<String> list = new ArrayList<>(); Stream<String> stream = list.stream(); //串行流 Stream<String> parallelStream = list.parallelStream(); //并行流
Arrays 中的 stream() 方法,將數(shù)組轉(zhuǎn)成流
Integer[] nums = new Integer[10]; Stream<Integer> stream = Arrays.stream(nums);
Stream中的靜態(tài)方法:of()、iterate()、generate()
Stream<Integer> stream = Stream.of(1,2,3,4,5,6); stream.forEach(System.out::println); // 輸出:1 2 3 4 5 6 Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(6); stream2.forEach(System.out::println); // 輸出:0 2 4 6 8 10 Stream<Double> stream3 = Stream.generate(Math::random).limit(2); stream3.forEach(System.out::println); // 輸出:兩個(gè)隨機(jī)數(shù)
BufferedReader.lines() 方法,將每行內(nèi)容轉(zhuǎn)成流
BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);
Pattern.splitAsStream() 方法,將字符串分隔成流
Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);
//輸出:a b c d
操作2:中間操作
篩選(過(guò)濾)、去重
方法
| 方法 | 說(shuō)明 |
|---|---|
| filter | 過(guò)濾流中的某些元素(只保留返回值為true的項(xiàng)) |
| limit(n) | 獲取前n個(gè)元素 |
| skip(n) | 跳過(guò)前n個(gè)元素,配合limit(n)可實(shí)現(xiàn)分頁(yè) |
| distinct | 通過(guò)流中元素的 hashCode() 和 equals() 去除重復(fù)元素 |
單個(gè)元素篩選(過(guò)濾)、去重、跳過(guò)、獲取前n個(gè)
List<Integer> list = new ArrayList<>(Arrays.asList(6, 4, 6, 7, 3, 9, 8, 10, 12, 14, 14));
List<Integer> newList = list.stream()
.filter(s -> s > 5) //6 6 7 9 8 10 12 14 14
.distinct() //6 7 9 8 10 12 14
.skip(2) //9 8 10 12 14
.limit(2) //9 8
.collect(Collectors.toList());
根據(jù)對(duì)象屬性去重
List<User> list = new ArrayList<User>() {{
add(new User("Tony", 20, "12"));
add(new User("Pepper", 20, "123"));
add(new User("Tony", 22, "1234"));
add(new User("Tony", 22, "12345"));
}};
//只通過(guò)名字去重
List<User> streamByNameList = list.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getName))), ArrayList::new
));
System.out.println(streamByNameList);
//[User{name='Pepper', age=20, Phone='123'},
// User{name='Tony', age=20, Phone='12'}]
//通過(guò)名字和年齡去重
List<User> streamByNameAndAgeList = list.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(
() -> new TreeSet<>(Comparator.comparing(o -> o.getName() + o.getAge()))), ArrayList::new
));
System.out.println(streamByNameAndAgeList);
//[User{name='Pepper', age=20, Phone='123'},
// User{name='Tony', age=20, Phone='12'},
// User{name='Tony', age=22, Phone='1234'}]
collectingAndThen 這個(gè)方法的意思是: 將收集的結(jié)果轉(zhuǎn)換為另一種類型。
因此上面的方法可以理解為:把 new TreeSet<>(Comparator.comparingLong(BookInfoVo::getRecordId))這個(gè)set轉(zhuǎn)換為 ArrayList。
映射
方法
| 方法 | 說(shuō)明 |
|---|---|
| map | 函數(shù)作為參數(shù),該函數(shù)被應(yīng)用到每個(gè)元素,并將其映射成一個(gè)新的元素。新值類型可以和原來(lái)的元素的類型不同。 |
| flatMap | 函數(shù)作為參數(shù),將流中每個(gè)值換成另一個(gè)流,再把所有流連成一個(gè)流。 新值類型可以和原來(lái)的元素的類型不同。 |
| mapToInt/Long/Double | 跟map差不多。只是將其轉(zhuǎn)為基本類型。 |
| flatMapToInt/Long/Double | 跟flatMap差不多。只是將其轉(zhuǎn)為基本類型。 |
新值類型和原來(lái)的元素的類型相同示例
List<String> list = Arrays.asList("a,b,c", "1,2,3");
//將每個(gè)元素轉(zhuǎn)成一個(gè)新的且不帶逗號(hào)的元素
Stream<String> s1 = list.stream().map(s -> s.replaceAll(",", ""));
s1.forEach(System.out::println);
// abc 123
Stream<String> s2 = list.stream().flatMap(s -> {
//將每個(gè)元素轉(zhuǎn)換成一個(gè)stream
String[] split = s.split(",");
Stream<String> s3 = Arrays.stream(split);
return s3;
});
s2.forEach(System.out::println);
// a b c 1 2 3
新值類型和原來(lái)的元素的類型不同示例
User u1 = new User("aa", 10);
User u2 = new User("bb", 20);
User u3 = new User("cc", 10);
List<User> list = Arrays.asList(u1, u2, u3);
Set<Integer> ageSet = list.stream().map(User::getAge).collect(Collectors.toSet());
ageSet.forEach(System.out::println);
//20 10
int[] ageInt = list.stream().map(User::getAge).mapToInt(Integer::intValue).toArray();
//下邊這樣也可以
//Integer[] ages = list.stream.map(User::getAge).toArray(Integer[]::new);
for (int i : ageInt) {
System.out.println(i);
}
//10 20 10
map的原型為:<R> Stream<R> map(Function<? super T, ? extends R> mapper);
上邊例子中,將Student::getAge作為參數(shù),其實(shí)際為:<R> Stream<Integer> map(Function<? super Student, ? extends Integer> mapper);
排序
方法
| 方法 | 說(shuō)明 |
|---|---|
| sorted() | 自然排序,流中元素需實(shí)現(xiàn)Comparable接口。 例:list.stream().sorted() |
| sorted(Comparator com) | 定制排序。常用以下幾種: list.stream().sorted(Comparator.reverseOrder()) list.stream().sorted(Comparator.comparing(Student::getAge)) list.stream().sorted(Comparator.comparing(Student::getAge).reversed()) |
示例
List<String> list = Arrays.asList("aa", "ff", "dd");
//String 類自身已實(shí)現(xiàn)Comparable接口
list.stream().sorted().forEach(System.out::println);
System.out.println("------------------------------------");
User u1 = new User("dd", 40);
User u2 = new User("bb", 20);
User u3 = new User("aa", 20);
User u4 = new User("aa", 30);
List<User> userList = Arrays.asList(u1, u2, u3, u4);
//按年齡升序
userList.stream().sorted(Comparator.comparing(User::getAge))
.forEach(System.out::println);
System.out.println("------------------------------------");
//先按年齡升序,年齡相同則按姓名升序
userList.stream().sorted(
(o1, o2) -> {
if (o1.getAge().equals(o2.getAge())) {
return o1.getName().compareTo(o2.getName());
} else {
return o1.getAge().compareTo(o2.getAge());
}
}
).forEach(System.out::println);
結(jié)果
aa
dd
ff
------------------------------------
User{name='bb', age=20}
User{name='aa', age=20}
User{name='aa', age=30}
User{name='dd', age=40}
------------------------------------
User{name='aa', age=20}
User{name='bb', age=20}
User{name='aa', age=30}
User{name='dd', age=40}
消費(fèi)
方法
| 方法 | 說(shuō)明 |
|---|---|
| peek | 類似于map,能得到流中的每一個(gè)元素。 但map接收的是一個(gè)Function表達(dá)式,有返回值; 而peek接收的是Consumer表達(dá)式,沒(méi)有返回值。 |
示例
User u1 = new User("dd", 40);
User u2 = new User("bb", 20);
User u3 = new User("aa", 20);
User u4 = new User("aa", 30);
List<User> list = Arrays.asList(u1, u2, u3, u4);
List<User> list1 = list.stream()
.peek(o -> o.setAge(100))
.collect(Collectors.toList());
System.out.println(list1);
結(jié)果:
[User{name='dd', age=100}, User{name='bb', age=100}, User{name='aa', age=100}, User{name='aa', age=100}]
操作3:終止操作
匹配、最值、個(gè)數(shù)
方法
| 方法 | 說(shuō)明 |
|---|---|
| allMatch | 接收一個(gè) Predicate 函數(shù),當(dāng)流中每個(gè)元素都符合該斷言時(shí)才返回true,否則返回false |
| noneMatch | 接收一個(gè) Predicate 函數(shù),當(dāng)流中每個(gè)元素都不符合該斷言時(shí)才返回true,否則返回false |
| anyMatch | 接收一個(gè) Predicate 函數(shù),只要流中有一個(gè)元素滿足該斷言則返回true,否則返回false |
| findFirst | 返回流中第一個(gè)元素 |
| findAny | 返回流中的任意元素 |
| count | 返回流中元素的總個(gè)數(shù) |
| max | 返回流中元素最大值 |
| min | 返回流中元素最小值 |
實(shí)例1:?jiǎn)蝹€(gè)類型
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); // 匹配 boolean allMatch = list.stream().allMatch(e -> e > 10); //false boolean noneMatch = list.stream().noneMatch(e -> e > 10); //true boolean anyMatch = list.stream().anyMatch(e -> e > 4); //true // 獲取第一個(gè)/第任意個(gè) Integer findFirst = list.stream().findFirst().get(); //1 Integer findAny = list.stream().findAny().get(); //1 // 計(jì)數(shù)、最大值、最小值 long count = list.stream().count(); //5 Integer max = list.stream().max(Integer::compareTo).get(); //5 Integer min = list.stream().min(Integer::compareTo).get(); //1
實(shí)例2:獲取對(duì)象中的字段的最值
User u1 = new User("dd", 40);
User u2 = new User("bb", 20);
User u3 = new User("aa", 20);
User u4 = new User("aa", 30);
List<User> list = Arrays.asList(u1, u2, u3, u4);
//獲取最小年齡的用戶。
User user1 = list.stream()
.min(Comparator.comparing(User::getAge))
.get();
System.out.println(user1);
System.out.println("------------------------------------");
//獲取先按姓名升序,姓名相同則按年齡升序。然后獲取最小的那個(gè)(第一個(gè))
User user = list.stream().min((o1, o2) -> {
if (o1.getAge().equals(o2.getAge())) {
return o1.getName().compareTo(o2.getName());
} else {
return o1.getAge().compareTo(o2.getAge());
}
}).get();
System.out.println(user);
結(jié)果
User{name='bb', age=20}
------------------------------------
User{name='aa', age=20}
收集
方法
| 方法 | 說(shuō)明 |
|---|---|
| collect | 接收一個(gè)Collector實(shí)例,將流中元素收集成另外一個(gè)數(shù)據(jù)結(jié)構(gòu)。 |
Collector實(shí)例一般由Collectors的靜態(tài)方法取得。例如:Collectors.toList()
公共代碼
User u1 = new User("dd", 40);
User u2 = new User("bb", 20);
User u3 = new User("aa", 20);
User u4 = new User("aa", 30);
List<User> list = Arrays.asList(u1, u2, u3, u4);
轉(zhuǎn)換
字符串分隔符連接
String joinName = list.stream().map(User::getName).collect(Collectors.joining(",", "(", ")"));
System.out.println(joinName);
//(dd,bb,aa,aa)
轉(zhuǎn)成list
List<Integer> ageList = list.stream().map(User::getAge).collect(Collectors.toList()); System.out.println(ageList); //[40, 20, 20, 30]
轉(zhuǎn)成set
Set<Integer> ageSet = list.stream().map(User::getAge).collect(Collectors.toSet()); System.out.println(ageSet); //[20, 40, 30]
轉(zhuǎn)成map(注:key不能相同,否則報(bào)錯(cuò))
User s1 = new User("dd", 40);
User s2 = new User("bb", 20);
User s3 = new User("aa", 20);
List<User> list = Arrays.asList(s1, s2, s3);
Map<String, Integer> ageMap = list.stream().collect(Collectors.toMap(User::getName, User::getAge));
System.out.println(ageMap);
//{aa=20, bb=20, dd=40}
本處我將重復(fù)的名字給去掉了一個(gè),因?yàn)槿绻鹝ey有重復(fù)的會(huì)報(bào)錯(cuò)。
三個(gè)參數(shù)的map
第一個(gè)參數(shù)就是用來(lái)生成key值的,第二個(gè)參數(shù)就是用來(lái)生成value值的。
第三個(gè)參數(shù)用在key值沖突的情況下:若新元素產(chǎn)生的key在Map中已經(jīng)出現(xiàn)過(guò)了,第三個(gè)參數(shù)就會(huì)定義解決的辦法。
User u1 = new User("aa", 10);
User u2 = new User("bb", 20);
User u3 = new User("cc", 10);
User u4 = new User("bb", 30);
List<User> list = new ArrayList<>(Arrays.asList(u1, u2, u3, u4));
Map<String, List<User>> listMap = list.stream().collect(Collectors.toMap(User::getName,
o -> {
List<User> list1 = new ArrayList<>();
list1.add(o);
return list1;
},
(r1, r2) -> {
r1.addAll(r2);
return r1;
}
)
);
System.out.println(listMap);
結(jié)果
{aa=[User{name='aa', age=20}, User{name='aa', age=30}], bb=[User{name='bb', age=20}], dd=[User{name='dd', age=40}]}
聚合
聚合(總數(shù)、平均值、最大最小值等)
//1.用戶總數(shù)
Long count = list.stream().collect(Collectors.counting());
System.out.println(count);
//4
//2.最大年齡 (最小的minBy同理)
Integer maxAge = list.stream().map(User::getAge).collect(Collectors.maxBy(Integer::compare)).get();
System.out.println(maxAge);
//40
//3.所有人的年齡
Integer sumAge = list.stream().collect(Collectors.summingInt(User::getAge));
System.out.println(sumAge);
//110
//4.平均年齡
Double averageAge = list.stream().collect(Collectors.averagingDouble(User::getAge));
System.out.println(averageAge);
// 27.5
// 統(tǒng)計(jì)上邊所有數(shù)據(jù)
DoubleSummaryStatistics stat = list.stream().collect(Collectors.summarizingDouble(User::getAge));
System.out.println("count:" + stat.getCount() + " max:" + stat.getMax() + " sum:" + stat.getSum()
+ " average:" + stat.getAverage());
//count:4 max:40.0 sum:110.0 average:27.5
分組
//根據(jù)年齡分組
Map<Integer, List<User>> listMap = list.stream().collect(Collectors.groupingBy(User::getAge));
for (Map.Entry<Integer, List<User>> entry : listMap.entrySet()) {
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
//20-->[User{name='bb', age=20}, User{name='aa', age=20}]
//40-->[User{name='dd', age=40}]
//30-->[User{name='aa', age=30}]
多重分組
// 先根據(jù)年齡分再根據(jù)
Map<Integer, Map<String, List<User>>> ageNameMap = list.stream().collect(
Collectors.groupingBy(User::getAge, Collectors.groupingBy(User::getName)));
for (Map.Entry<Integer, Map<String, List<User>>> entry : ageNameMap.entrySet()) {
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
//20-->{aa=[User{name='aa', age=20}], bb=[User{name='bb', age=20}]}
//40-->{dd=[User{name='dd', age=40}]}
//30-->{aa=[User{name='aa', age=30}]}
分區(qū)
特殊的分組,分為true和false兩組
//分成兩部分,一部分大于10歲,一部分小于等于10歲
Map<Boolean, List<User>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 20));
for (Map.Entry<Boolean, List<User>> entry : partMap.entrySet()) {
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
//false-->[User{name='bb', age=20}, User{name='aa', age=20}]
//true-->[User{name='dd', age=40}, User{name='aa', age=30}]
總結(jié)
Collector<T, A, R> 是一個(gè)接口,有以下5個(gè)抽象方法:
1.Supplier<A> supplier():創(chuàng)建一個(gè)結(jié)果容器A
2.BiConsumer<A, T> accumulator():消費(fèi)型接口,第一個(gè)參數(shù)為容器A,第二個(gè)參數(shù)為流中元素T。
3.BinaryOperator<A> combiner():函數(shù)接口,該參數(shù)的作用跟上一個(gè)方法(reduce)中的combiner參數(shù)一樣,將并行流中各個(gè)子進(jìn)程的運(yùn)行結(jié)果(accumulator函數(shù)操作后的容器A)進(jìn)行合并。
4.Function<A, R> finisher():函數(shù)式接口,參數(shù)為:容器A,返回類型為:collect方法最終想要的結(jié)果R。
5.Set<Characteristics> characteristics():返回一個(gè)不可變的Set集合,表明該Collector的特征。有以下三個(gè)特征:
- CONCURRENT:表示此收集器支持并發(fā)。(官方文檔還有其他描述,暫時(shí)沒(méi)去探索,故不作過(guò)多翻譯)
- UNORDERED:表示該收集操作不會(huì)保留流中元素原有的順序。
- IDENTITY_FINISH:表示finisher參數(shù)只是標(biāo)識(shí)而已,可忽略。
注:如果對(duì)以上函數(shù)接口不太理解的話,可參考:Java中Lambda表達(dá)式的使用詳細(xì)教程
Collectors.toList() 解析
//toList 源碼
public static <T> Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> {
left.addAll(right);
return left;
}, CH_ID);
}
//為了更好地理解,我們轉(zhuǎn)化一下源碼中的lambda表達(dá)式
public <T> Collector<T, ?, List<T>> toList() {
Supplier<List<T>> supplier = () -> new ArrayList();
BiConsumer<List<T>, T> accumulator = (list, t) -> list.add(t);
BinaryOperator<List<T>> combiner = (list1, list2) -> {
list1.addAll(list2);
return list1;
};
Function<List<T>, List<T>> finisher = (list) -> list;
Set<Collector.Characteristics> characteristics = Collections.unmodifiableSet
(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
return new Collector<T, List<T>, List<T>>() {
@Override
public Supplier supplier() {
return supplier;
}
@Override
public BiConsumer accumulator() {
return accumulator;
}
@Override
public BinaryOperator combiner() {
return combiner;
}
@Override
public Function finisher() {
return finisher;
}
@Override
public Set<Characteristics> characteristics() {
return characteristics;
}
};
}
規(guī)約
方法
| 方法 | 說(shuō)明 |
|---|---|
| Optional<T> reduce(BinaryOperator<T> accumulator) | 第一次執(zhí)行時(shí),accumulator函數(shù)的第一個(gè)參數(shù)為流中的第一個(gè)元素,第二個(gè)參數(shù)為流中元素的第二個(gè)元素; 第二次執(zhí)行時(shí),第一個(gè)參數(shù)為第一次函數(shù)執(zhí)行的結(jié)果,第二個(gè)參數(shù)為流中的第三個(gè)元素; 依次類推。 |
| T reduce(T identity, BinaryOperator<T> accumulator) | 流程跟上面一樣,只是第一次執(zhí)行時(shí),accumulator函數(shù)的第一個(gè)參數(shù)為identity,而第二個(gè)參數(shù)為流中的第一個(gè)元素。 |
| <U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner) | 在串行流(stream)中,該方法跟第二個(gè)方法一樣,即第三個(gè)參數(shù)combiner不會(huì)起作用。在并行流(parallelStream)中,我們知道流被fork join出多個(gè)線程進(jìn)行執(zhí)行,此時(shí)每個(gè)線程的執(zhí)行流程就跟第二個(gè)方法reduce(identity,accumulator)一樣,而第三個(gè)參數(shù)combiner函數(shù),則是將每個(gè)線程的執(zhí)行結(jié)果當(dāng)成一個(gè)新的流,然后使用第一個(gè)方法reduce(accumulator)流程進(jìn)行規(guī)約。 |
示例
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Integer v = list.stream().reduce((x1, x2) -> x1 + x2).get();
System.out.println(v);
// 15
Integer v0 = list.stream().reduce((x1, x2) -> x1 + x2).get();
System.out.println(v0);
//15
Integer v1 = list.stream().reduce(10, (x1, x2) -> x1 + x2);
System.out.println(v1);
//25
Integer v2 = list.stream().reduce(0,
(x1, x2) -> {
System.out.println("stream accumulator: x1:" + x1 + " x2:" + x2);
return x1 - x2;
},
(x1, x2) -> {
System.out.println("stream combiner: x1:" + x1 + " x2:" + x2);
return x1 * x2;
});
System.out.println(v2);
// -15
Integer v3 = list.parallelStream().reduce(0,
(x1, x2) -> {
System.out.println("parallelStream accumulator: x1:" + x1 + " x2:" + x2);
return x1 - x2;
},
(x1, x2) -> {
System.out.println("parallelStream combiner: x1:" + x1 + " x2:" + x2);
return x1 * x2;
});
System.out.println(v3);
//-120打印結(jié)果為:
15
15
25
stream accumulator: x1:0 x2:1
stream accumulator: x1:-1 x2:2
stream accumulator: x1:-3 x2:3
stream accumulator: x1:-6 x2:4
stream accumulator: x1:-10 x2:5
-15
parallelStream accumulator: x1:0 x2:3
parallelStream accumulator: x1:0 x2:5
parallelStream accumulator: x1:0 x2:4
parallelStream combiner: x1:-4 x2:-5
parallelStream accumulator: x1:0 x2:2
parallelStream accumulator: x1:0 x2:1
parallelStream combiner: x1:-3 x2:20
parallelStream combiner: x1:-1 x2:-2
parallelStream combiner: x1:2 x2:-60
-120
到此這篇關(guān)于一文詳解Java中Stream流的使用的文章就介紹到這了,更多相關(guān)Java Stream流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java基礎(chǔ)-Java基本數(shù)據(jù)類型
這篇文章主要介紹了Java基礎(chǔ)-Java基本數(shù)據(jù)類型,變量就是申請(qǐng)內(nèi)存來(lái)存儲(chǔ)值。也就是說(shuō),當(dāng)創(chuàng)建變量的時(shí)候,需要在內(nèi)存中申請(qǐng)空間,下面我們就來(lái)對(duì)Java基本數(shù)據(jù)類型作簡(jiǎn)單的介紹,需要的朋友可以參考一下2022-01-01
java中對(duì)象為null時(shí)的打印輸出方式
這篇文章主要介紹了java中對(duì)象為null時(shí)的打印輸出方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
SpringBoot視圖解析實(shí)現(xiàn)原理深入分析
視圖解析其實(shí)就是SpringBoot某一個(gè)controller的方法執(zhí)行完成之后,它是跳轉(zhuǎn)到那個(gè)頁(yè)面。由于我們springboot項(xiàng)目默認(rèn)打包為jar包,是形成壓縮包的形式,而jsp又不支持壓縮,所以我們SpringBoot不知JSP的,需要引入第三方模板引擎才可以處理2022-10-10
Java實(shí)現(xiàn)根據(jù)前端所要格式返回樹(shù)形3級(jí)層級(jí)數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)根據(jù)前端所要格式返回樹(shù)形3級(jí)層級(jí)數(shù)據(jù),文中的示例代碼講解詳細(xì),有需要的小伙伴可以了解下2024-02-02
Spring session實(shí)現(xiàn)共享單點(diǎn)登錄案例過(guò)程解析
這篇文章主要介紹了Spring session實(shí)現(xiàn)共享單點(diǎn)登錄案例過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07

