1.遍歷/匹配(foreach/find/match) Stream也是支持類似集合的遍歷和匹配元素的,只是Stream中的元素是以Optional類型存在的。Stream的遍歷、匹配非常簡單。 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 7, ...
1.遍歷/匹配(foreach/find/match)
Stream
也是支持類似集合的遍歷和匹配元素的,只是Stream
中的元素是以Optional
類型存在的。Stream
的遍歷、匹配非常簡單。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 7, 8, 9, 0, 100);
// find使用,查找第一個元素
Optional<Integer> first = list.stream().findFirst();
log.info(first.get().toString());
// match使用,判斷是否存在某個值
boolean b1 = list.stream().anyMatch(value -> value >= 100);
boolean b2 = list.stream().anyMatch(value -> value > 10);
log.info(String.valueOf(b1));
log.info(String.valueOf(b2));
// foreach使用,遍歷輸出元素
list.stream().filter(value -> value > 4).forEach(System.out::print);
System.out.println();
list.forEach(System.out::print);
2.篩選(filter)
篩選,是按照一定的規則校驗流中的元素,將符合條件的元素提取到新的流中的操作。
// 數字篩選
List<Integer> list = Arrays.asList(1, 3, 4, 5, 6, 7, 8, 9, 10, 20);
ArrayList<Integer> arrayList1 = new ArrayList<>();
list.stream().filter(value -> value > 4).forEach(value -> arrayList1.add(value));
log.info(arrayList1.toString());
ArrayList<Integer> arrayList2 = new ArrayList<>();
list.stream().filter(value -> value <= 4).forEach(arrayList2::add);
log.info(arrayList2.toString());
// 對象篩選
List<User> userList1 = Arrays.asList(
new User(1, "xw", "男"),
new User(2, "zgx", "男"),
new User(3, "gg", "男"),
new User(4, "whb", "男"),
new User(5, "yda", "男"),
new User(6, "bhm", "女")
);
List<User> userList2 = new ArrayList<>();
userList1.stream().filter(user -> user.getId() > 2).forEach(userList2::add);
log.info(userList2.toString());
userList1.stream().filter(user -> user.getName().equals("xw")).forEach(System.out::println);
HashMap<String, Optional<User>> userHashMap = new HashMap<>();
Optional<User> man = userList1.stream().filter(user -> user.getSex().equals("男")).findFirst();
userHashMap.put("man", man);
log.info(userHashMap.toString());
3.聚合(max/min/count)
max
、min
、count
這些字眼你一定不陌生,沒錯,在mysql中我們常用它們進行數據統計。Java stream中也引入了這些概念和用法,極大地方便了我們對集合、數組的數據統計工作。
max、min、count
// max & min & count
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 89, 9, 0, 10, 20, 30);
Optional<Integer> max = list.stream()
.max(Comparator.comparing(Integer::intValue));
log.info(String.format("最大值是:%d", max.get()));
Optional<Integer> min = list.stream()
.min(Comparator.comparing(value -> value.intValue()));
log.info(String.format("最小值是:%d", min.get()));
Integer count1 = Math.toIntExact(list.stream().count());
log.info(String.format("list總元素量1為:%d", count1));
Integer count2 = Math.toIntExact(list.stream().filter(value -> value > 5).count());
log.info(String.format("list元素值大於5的個數:%d", count2));
List<User> userList = Arrays.asList(
new User(1, "xw", "男", 22),
new User(2, "zgx", "男", 22),
new User(3, "whb", "男", 23),
new User(4, "gg", "男", 30),
new User(5, "yda", "男", 22),
new User(6, "bhm", "女", 22),
new User(7, "lwn", "女", 22)
);
Optional<User> ageMax = userList.stream().max(Comparator.comparing(value -> value.getAge()));
log.info(String.format("年齡最大的是:%s", ageMax.get()));
Optional<User> ageMin = userList.stream().filter(user -> user.getSex().equals("男")).min(Comparator.comparing(User::getAge));
log.info(String.format("性別為男且年齡最小的:%s", ageMin.get()));
Integer count3 = Math.toIntExact(userList.stream().filter(user -> user.getAge() > 22).count());
log.info(String.format("年齡大於22的用戶數量為:%d", count3));
4.映射(map/flatMap)
映射,可以將一個流的元素按照一定的映射規則映射到另一個流中。分為map
和flatMap
:
map
:接收一個函數作為參數,該函數會被應用到每個元素上,並將其映射成一個新的元素。flatMap
:接收一個函數作為參數,將流中的每個值都換成另一個流,然後把所有流連接成一個流。
map
// map
List<Integer> list1 = Arrays.asList(1, 3, 5, 6, 7, 8, 0, 10, 20, 22, 39);
List<Integer> collect1 = list1.stream().filter(value -> value > 7).collect(Collectors.toList());
log.info(String.format("list1元素值大於7的有: %s", collect1));
List<String> list2 = Arrays.asList("xw", "sjdk", "sf", "jk", "hoksh", "shdfj", "jhgkj");
List<String> collect2 = list2.stream().map(String::toUpperCase).collect(Collectors.toList());
log.info(String.format("list2元素值全轉大寫,結果:%s", collect2));
List<User> userList1 = Arrays.asList(
new User(1, "xw", "男", 22),
new User(2, "zgx", "男", 22),
new User(3, "whb", "男", 23),
new User(4, "gg", "男", 30),
new User(5, "yda", "男", 22),
new User(6, "bhm", "女", 22),
new User(7, "lwn", "女", 22),
new User(8, "ksj", "女", 22)
);
List<User> userList2 = userList1.stream()
.map(user -> {
if (user.getSex().equals("女")) {
user.setAge(user.getAge() - 2);
}
user.setName(user.getName().toUpperCase());
return user;
})
.filter(user -> user.getAge() > 19 && user.getSex().equals("女"))
.collect(Collectors.toList());
log.info(String.format("修改結果為:%s", userList2));
flatMap
// flatMap
List<String> stringList = userList1.stream()
.flatMap(user -> {
Stream<String> stream = Arrays.stream(user.toString().split("="));
return stream;
})
.collect(Collectors.toList());
log.info(String.format("flatMap處理前:%s", userList1));
log.info(String.format("flatMap轉換結果:%s", stringList));
5.歸約(reduce)
歸約,也稱縮減,顧名思義,是把一個流縮減成一個值,能實現對集合求和
、求乘積
和求最值
操作。
// 求和
List<Integer> list1 = Arrays.asList(1, 3, 5, 2, 1, 5, 89, 23, 89, 23, 34);
Integer sum = list1.stream().reduce(0, Integer::sum);
log.info(String.format("list1中各元素之和:%d", sum));
// 求積
List<Integer> list2 = Arrays.asList(1, 2, 4);
Optional<Integer> product = list2.stream().reduce((x, y) -> x * y);
log.info(String.format("list中2各元素之積:%d", product.get()));
// 求最大值1
Optional<Integer> max1 = list1.stream().reduce(Integer::max);
log.info(String.format("list1中的最大值是:%d", max1.get()));
// 求最大/小值2
Optional<Integer> min1 = list1.stream().reduce((x, y) -> x < y ? x : y);
log.info(String.format("list1中的最小值:%d", min1.get()));
List<User> userList1 = Arrays.asList(
new User(1, "xw", "男", 22),
new User(2, "zgx", "男", 22),
new User(3, "whb", "男", 23),
new User(4, "gg", "男", 30),
new User(5, "yda", "男", 22),
new User(6, "bhm", "女", 23),
new User(7, "lsn", "女", 22),
new User(8, "ksj", "女", 22)
);
Integer maxAge1 = userList1.stream().reduce(0, (maxAge, user) -> maxAge > user.getAge() ? maxAge : user.getAge(), Integer::max);
log.info(String.format("年齡最大是:%d", maxAge1));
Optional<Integer> max2 = userList1.stream().map(User::getAge).reduce(Integer::max);
Optional<Integer> max3 = userList1.stream().map(User::getAge).reduce((x, y) -> x > y ? x : y);
log.info(String.format("年齡最大是:%d", max2.get()));
log.info(String.format("年齡最大是:%d", max3.get()));
Integer stringMaxLength = userList1.stream()
.filter(user -> user.getAge() > 22 && user.getAge() < 25)
.flatMap(user -> {
Stream<String> newStream = Arrays.stream(user.toString().split("="));
return newStream;
})
.collect(Collectors.toList())
.stream().map(String::toUpperCase)
.reduce(0, (maxLength, string) -> maxLength > string.length() ? maxLength : string.length(), Integer::max);
log.info(String.format("最大字元串長度為:%s", stringMaxLength));
6.收集(collect)
collect
,收集,可以說是內容最繁多、功能最豐富的部分了。從字面上去理解,就是把一個流收集起來,最終可以是收集成一個值也可以收集成一個新的集合。
collect主要依賴java.util.stream.Collectors類內置的靜態方法。
6.1歸集(toList/toSet/toMap)
因為流不存儲數據,那麼在流中的數據完成處理後,需要將流中的數據重新歸集到新的集合里。toList
、toSet
和toMap
比較常用,另外還有toCollection
、toConcurrentMap
等複雜一些的用法。
toList
List<Integer> list1 = Arrays.asList(1, 2, 8, 0, 9, 0, 1, 23, 32, 37, 49, 48);
List<Integer> collect1 = list1.stream().filter(value -> value > 5).collect(Collectors.toList());
log.info(String.format("list1中收集>5的結果為:%s", collect1));
toSet
Set<Integer> collect2 = list1.stream().filter(value -> value < 5).collect(Collectors.toSet());
log.info(String.format("list1中收集<5的結果為:%s", collect2));
collect2.forEach(value -> {System.out.print(value + " ");
toMap
List<User> userList1 = Arrays.asList(
new User(1, "xww", "男", 22),
new User(2, "zgx", "男", 22),
new User(3, "whb", "男", 23),
new User(4, "gg", "男", 30),
new User(5, "yda", "男", 22),
new User(6, "bhm", "女", 23),
new User(7, "lsn", "女", 22),
new User(8, "ksj", "女", 22)
);
Map<String, User> userMap = userList1.stream()
.filter(user -> user.getSex().equals("女"))
.collect(Collectors.toMap(User::getName, user -> user));
log.info(String.format("性別為女的用戶轉map:%s", userMap));
6.2 統計(count/averaging)
Collectors
提供了一系列用於數據統計的靜態方法:
- 計數:
count
- 平均值:
averagingInt
、averagingLong
、averagingDouble
- 最值:
maxBy
、minBy
- 求和:
summingInt
、summingLong
、summingDouble
- 統計以上所有:
summarizingInt
、summarizingLong
、summarizingDouble
List<Integer> list = Arrays.asList(1, 2, 3, 2, 5, 3, 9, 8, 7, 6, 29, 10, 22);
// count
long count = list.stream().filter(value -> value > 10).count();
log.info(String.format("list中元素>10的個數為:%d", count));
// average
Double average = list.stream().filter(value -> value > 1).collect(Collectors.averagingInt(Integer::intValue));
log.info(String.format("list中元素>1的元素平均值為:%.2f", average));
List<User> userList1 = Arrays.asList(
new User(1, "xww", "女", 22),
new User(2, "zgx", "男", 22),
new User(3, "whb", "男", 23),
new User(4, "gg", "男", 30),
new User(5, "yda", "男", 22),
new User(6, "bhm", "女", 23),
new User(7, "lsn", "女", 22),
new User(8, "ksj", "女", 22)
);
Double averageAge = userList1.stream()
.filter(user -> user.getSex().equals("男"))
.map(User::getAge)
.collect(Collectors.averagingInt(Integer::intValue));
log.info(String.format("男用戶的平均年齡為:%d 歲", averageAge.intValue()));
// mapToInt
int ageSum = userList1.stream()
.filter(user -> user.getSex().equals("女"))
.mapToInt(User::getAge)
.sum();
log.info(String.format("女用戶的年齡之和為:%d", ageSum));
// summarizingInt 統計 計數、總和、最小值、平均值、最大值
IntSummaryStatistics recording = userList1.stream()
.filter(user -> user.getSex().equals("男"))
.collect(Collectors.summarizingInt(User::getAge));
log.info(String.format("記錄所有男用戶的年齡各項值,結果為:%s", recording));
6.3 分組(partitioningBy/groupingBy)
- 分區:將
stream
按條件分為兩個Map
,比如員工按薪資是否高於8000分為兩部分。 - 分組:將集合分為多個Map,比如員工按性別分組。有單級分組和多級分組。
partitioningBy
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 20, 37, 49, 243, 30);
// partitioningBy
Map<Boolean, List<Integer>> collect1 = list.stream()
.collect(Collectors.partitioningBy(value -> value > 20));
log.info(String.format("元素值是否大於20進行分組,結果為:%s", collect1));
collect1.forEach((key, value) -> {
log.info(String.format("元素值是否大於20進行分組,結果為:%s:%s", key, value));
});
groupingBy
List<User> userList = Arrays.asList(
new User(1, "xww", "女", 22),
new User(2, "zgx", "男", 21),
new User(3, "whb", "男", 23),
new User(4, "gg", "男", 30),
new User(5, "yda", "男", 22),
new User(6, "bhm", "女", 23),
new User(7, "lsn", "女", 22),
new User(8, "ksj", "女", 22)
);
// groupingBy
Map<String, List<User>> collect2 = userList.stream()
.collect(Collectors.groupingBy(User::getSex));
log.info(String.format("根據性別對用戶進行分組,結果為:%s", collect2));
collect2.forEach((key, user) -> {
log.info(String.format("根據性別對用戶進行分組,結果為:%s:%s", key, user));
});
6.4 接合(joining)
joining
可以將stream中的元素用特定的連接符(沒有的話,則直接連接)連接成一個字元串。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 20, 37, 49, 243, 30);
String collect = list.stream()
.map(Object::toString)
.collect(Collectors.joining("——"));
log.info(String.format("joining測試結果為:%s", collect));
7.排序(sorted)
sorted,中間操作。有兩種排序:
- sorted():自然排序,流中元素需實現Comparable介面
- sorted(Comparator com):Comparator排序器自定義排序
List<User> userList1 = Arrays.asList(
new User(1, "xw", "女", 22),
new User(2, "zgx", "男", 21),
new User(3, "whb", "男", 23),
new User(4, "gg", "男", 30),
new User(5, "yda", "男", 22),
new User(6, "bhm", "女", 23),
new User(7, "lsn", "女", 22),
new User(8, "ksj", "女", 22)
);
// sorted
List<User> userList2 = userList1.stream()
.sorted(Comparator.comparing(User::getAge))
.collect(Collectors.toList());
log.info(String.format("按照年齡排序,結果為:%s", userList2));
// 從小到大,正序
List<String> userName1 = userList1.stream()
.sorted(Comparator.comparing(User::getAge))
.map(User::getName)
.collect(Collectors.toList());
log.info(String.format("根據年齡從小到大排序:%s", userName1));
// 從大到小,倒序
List<String> userName2 = userList1.stream()
.filter(user -> user.getSex().equals("男"))
.sorted(Comparator.comparing(User::getAge).reversed())
.map(User::getName)
.collect(Collectors.toList());
log.info(String.format("男用戶根據年齡從大到小排序:%s", userName2));
8.提取/組合
流也可以進行合併
、去重
、限制
、跳過
等操作。
1.去重排序
List<Integer> list = Arrays.asList(1, 2, 4, 4, 10, 9, 6, 8, 6, 2, 3, 7, 5);
List<Integer> collect = list
.stream()
.distinct()
.sorted(Comparator.comparing(Integer::intValue))
.collect(Collectors.toList());
collect.forEach(x -> System.out.print(x+" ")); // 1 2 3 4 5 6 7 8 9 10
存在重覆數據的問題,這裡使用stream流的衍生功能,去除一個對象中的部分元素的重覆如下:
List<User> userList = Arrays.asList(
new User(1, "xw", "女", 21),
new User(2, "zgx", "男", 21),
new User(3, "whb", "男", 23),
new User(4, "gag", "男", 30),
new User(4, "gbg", "男", 30),
new User(4, "gcg", "女", 30),
new User(5, "yda", "男", 22),
new User(6, "bhm", "女", 23),
new User(7, "lsn", "女", 22),
new User(8, "ksj", "女", 22)
);
ArrayList<User> collect1 = userList.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(
Comparator.comparing(
User::getId))), ArrayList::new));
多個欄位或者多個條件去重
ArrayList<User> collect2 = userList.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(
Comparator.comparing(user->user.getName() + ";" + user.getId()))), ArrayList::new)
以上使用到了collectingAndThen()根據屬性進行去重的操作,進行結果集的收集,收集到結果集之後再進行下一步的處理。在這個去重操作中還用到了toCollection、TreeSet兩個操作。
public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,Function<R,RR> finisher)
看源碼中需要傳的參數有兩個,第一個參數是Collector的子類,所以Collectors類中的大多數方法都能使用,比如:toList(),toSet(),toMap()等,當然也包括collectingAndThen()。第二個參數是一個Function函數,也是去重的關鍵,用到的ArrayList::new調用到了ArrayList的有參構造。Function函數是R apply(T t),在第一個參數downstream放在第二個參數Function函數的參數裡面,將結果設置為t。對於toCollection是一個通用的方法,滿足treeSet收集集合,再傳入需要根據某個屬性進行比較的比較器,就能達到去重的效果。
2.限制長度(limit)
List<Integer> list = Arrays.asList(1, 2, 7, 3, 2, 2, 3, 4, 5, 2, 5, 6, 7, 8, 9, 0, 12);
List<Integer> collect1 = list
.stream()
.distinct()
.sorted(Comparator.comparing(Integer::intValue))
.limit(6)
.collect(Collectors.toList());
collect1.forEach(x -> System.out.print(x + " "));
3.跳過(skip)
// 跳過前幾項
List<Integer> list = Arrays.asList(1, 2, 7, 3, 2, 2, 3, 4, 5, 2, 5, 6, 7, 8, 9, 0, 12);
List<Integer> collect2 = list
.stream()
.distinct()
.sorted(Comparator.comparing(Integer::intValue))
.skip(3)
.limit(6)
.collect(Collectors.toList());
collect2.forEach(x -> System.out.print(x + " "));