Stream 使用一種類似用 SQL 語(yǔ)句從數(shù)據(jù)庫(kù)查詢數(shù)據(jù)的直觀方式來(lái)提供一種對(duì) Java 集合運(yùn)算和表達(dá)的高階抽象, 是一個(gè)來(lái)自數(shù)據(jù)源的元素隊(duì)列并支持聚合操作(https://upload-images.jianshu.io/upload_images/6348370-f8218715507512e1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Stream 可以對(duì)集合和數(shù)組進(jìn)行一些簡(jiǎn)化操作,將集合或數(shù)組轉(zhuǎn)化為Stream流宝冕,使用Stream流中的方法對(duì)集合/數(shù)組
中的元素進(jìn)行操作,解決集合中的一些弊端
和以前的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或者增強(qiáng)for的方式珊燎,顯示的在集合外部進(jìn)行迭代矮瘟,這叫做外部迭代庐扫,Stream提供了內(nèi)部迭代的方式饭望,流可以直接調(diào)用遍歷方法
- 當(dāng)使用一個(gè)流的時(shí)候,通常包括3個(gè)步驟:獲取一個(gè)數(shù)據(jù)源(source) -> 數(shù)據(jù)轉(zhuǎn)換 -> 執(zhí)行操作獲取想要的結(jié)果,每次轉(zhuǎn)換原有Stream對(duì)象形庭,返回一個(gè)新的Stream對(duì)象铅辞,這就允許對(duì)其操作可以向鏈條一樣排列,變成一個(gè)管道
- Stream兩種使用方法:
1.在java.util.Collection 中有一個(gè)方法 default Stream<E>
2.java.util.stream接口的靜態(tài)方法of可以獲取數(shù)組對(duì)應(yīng)的流
static <T> Stream<T> of (T... values)
List<String> list = new ArrayList<String>();
Stream<String> stream1 = list.stream();
Set<String> set = new HashSet<String>();
Stream<String> stream2 = set.stream();
Map<String, String> map = new HashMap<String, String>();
Set<String> keySet= map.keySet();
Stream<String> stream3 = keySet.stream();
Collection<String> collection = map.values();
Stream<String> stream4 = collection.stream();
// 獲取鍵值對(duì)的映射關(guān)系
// Map.entrySet() 這個(gè)方法返回的是一個(gè)Set<Map.Entry<K,V>>碘勉,Map.Entry 是Map中的一個(gè)接口巷挥,他的用途是表示一個(gè)映射項(xiàng)(里面有Key和Value),
// 而Set<Map.Entry<K,V>>表示一個(gè)映射項(xiàng)的Set验靡。Map.Entry里有相應(yīng)的getKey和getValue方法倍宾,即JavaBean雏节,讓我們能夠從一個(gè)項(xiàng)中取出Key和Value
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> stream = entries.stream();
// 數(shù)組轉(zhuǎn)換為stream流
Integer[] arr = {1,2,3,4,5};
Stream<Integer> stream5= Stream.of(arr);
- 案例1 : 對(duì)列表/數(shù)組中的每個(gè)元素都乘以2
package NewFeature.JavaStream;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public static void main(String[] args) {
int[] list = IntStream.range(1, 10).map(i -> i*2).toArray();
for (int value: list) {
System.out.println(value);
}
// or
List<Integer> result = IntStream.range(10, 20).map(i -> i*2).boxed().collect(Collectors.toList());
for (int value: result) {
System.out.println(value);
}
}
輸出:
2
4
6
8
10
12
14
16
18
- 案例2:篩選重復(fù)元素
List<Integer> list = Arrays.asList(1,2,2,3,4,2,1,5);
list.stream().filter(i -> i%2 == 0).distinct().forEach(System.out::println);
輸出:
2
4
- 選出以"張"開頭、長(zhǎng)度為3的名字
List<String> list = new ArrayList<String>();
list.add("張三豐");
list.add("馬可波諾");
list.add("張小龍");
list.add("趙無(wú)極");
list.add("青龍");
list.add("喬峰");
List<String> shortList = new ArrayList<>();
for (String name:list) {
if (name.length() == 3 && name.startsWith("張")) {
shortList.add(name);
}
}
System.out.println("張開頭并且長(zhǎng)度為2的名字=" + shortList.toString())
list.stream().filter(value -> value.startsWith("張"))
.filter(value -> value.length() == 3)
.collect(Collectors.toList()).forEach(System.out::print);
- 案例3:限制元素?cái)?shù)量
List<Integer> list = Arrays.asList(2,5,6,6,8,2,12,63,53,13,76);
list.stream().filter(i -> i>5).
distinct().
limit(3).
// skip(1).
collect(Collectors.toList()).
forEach(System.out::println);
輸出:
6
8
12
// 加了skip(1)
8
12
- 案例4: Stream支持map方法高职,接收一個(gè)函數(shù)作為參數(shù)钩乍,這個(gè)函數(shù)被應(yīng)用到每個(gè)元素上,并將其映射成一個(gè)新元素
List<String> list = Arrays.asList("wudy", "macBook", "china hello");
List<Integer> wordLens = list.stream().map(String::length).collect(Collectors.toList());
for (Integer item:wordLens) {
System.out.println("長(zhǎng)度=" + item);
}
輸出:
長(zhǎng)度=4
長(zhǎng)度=7
長(zhǎng)度=11
- 案例5:flatmap方法讓你把一個(gè)流中的每個(gè)值都換成另一個(gè)流怔锌,然后把所有的流連接起來(lái)成為一個(gè)流
List<String> list = Arrays.asList("Hello", "World");
list.stream().map(word -> word.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList())
.forEach(System.out::print)
輸出:
HeloWrd
如下圖:
-案例6:給定兩個(gè)數(shù)字列表寥粹,返回所有的數(shù)對(duì)?例如:給定列表[1,3]埃元、[2,4,6], 應(yīng)該返回[(1,2),(1,4),(1,6),(3,2),(3,4),(3,6)]
List<Integer> list1 = Arrays.asList(1,3);
List<Integer> list2 = Arrays.asList(2,4,6);
List<int[]> pairs = list1.stream().flatMap(i -> list2.stream()
.map(j -> new int[] {i, j}))
.collect(Collectors.toList());
// pairs.stream().forEach(item -> Arrays.stream(item).forEach();
for (int [] item: pairs) {
System.out.println(Arrays.toString(item));
}
輸出:
[1, 2]
[1, 4]
[1, 6]
[3, 2]
[3, 4]
[3, 6]
- 案例7: 匹配給定的謂詞
List<String> list = Arrays.asList("wudy","peter");
if (list.stream().anyMatch(value -> value.equals("peter"))) {
System.out.println("匹配到了給定的謂詞");
}
輸出:
匹配到了給定的謂詞
- 案例8: 流中是否所有的元素能匹配給定的謂詞
List<Integer> list = Arrays.asList(5,7,11,423,54);
if (list.stream().allMatch(value -> value > 3)) {
System.out.println("匹配了所有的謂詞");
}
輸出:
匹配了所有的謂詞
- 案例9: 流中沒有任何元素能匹配給定的謂詞
List<Integer> list = Arrays.asList(101,198,112,423,354);
if (list.stream().noneMatch(value -> value < 100)) {
System.out.println("都能活到100歲");
}
輸出:
都能活到100歲
- 案例10: 求Sum涝涤、max
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
// Integer sum = list.stream().reduce(0, (a, b) -> a + b);
Integer sum = list.stream().reduce(0, Integer::sum);
System.out.println("sum=" + sum);
Integer max = list.stream().reduce(0, Integer::max);
System.out.println("max=" + max);
輸出:
sum=21
max=6
- 案例11:現(xiàn)在有一個(gè)List集合,想對(duì)該集合中的數(shù)據(jù)分組處理
package NewFeature.JavaStream;
public class User {
private Integer id;
private Integer age;
private String name;
public User(Integer id, Integer age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public Integer getId() {
return id;
}
public Integer getAge() {
return age;
}
public String getName() {
return name;
}
public void setId(Integer id) {
this.id = id;
}
public void setAge(Integer age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
}
package NewFeature.JavaStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 現(xiàn)在有一個(gè)List集合岛杀,想對(duì)該集合中的數(shù)據(jù)分組處理
*/
public class StreamDemo {
public static void main(String[] args) {
List<User> list = getUserList();
Map<Integer, List<User>> userGroupList = list.stream().collect(Collectors.groupingBy(User::getAge));
System.out.println(userGroupList);
// for (Integer key: userGroupList.keySet()) {
// System.out.println(userGroupList.get(key));
// }
//
// for (List<User> value: userGroupList.values()) {
// System.out.println(value);
// }
}
public static ArrayList<User> getUserList() {
User user1 = new User(1, 18, "wudy");
User user2 = new User(2, 20, "peter");
User user3 = new User(3, 30, "timo");
User user4 = new User(4, 20, "chimmy");
ArrayList<User> arrayList = new ArrayList<User>();
arrayList.add(user1);
arrayList.add(user2);
arrayList.add(user3);
arrayList.add(user4);
return arrayList;
}
}
輸出:
{18=[NewFeature.JavaStream.User@5b6f7412], 20=[NewFeature.JavaStream.User@27973e9b, NewFeature.JavaStream.User@312b1dae], 30=[NewFeature.JavaStream.User@7530d0a]}
- Java8中Stream對(duì)列表去重的幾種方法
1.distinct() 對(duì)于 String 列表的去重
@Test
public void stream1(){
List<String> list = new ArrayList<>();
list.add("A");
list.add("C");
list.add("B");
list.add("C");
System.out.println("去重前=" + list);
list = list.stream().distinct().collect(Collectors.toList());
System.out.println("去重后=" + list);
}
去重前=[A, C, B, C]
去重后=[A, C, B]
2.對(duì)實(shí)體類列表的去重
// 定義實(shí)體類
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "message")
public class Message implements Serializable {
@ApiModelProperty(value = "用戶司機(jī)企業(yè)的id")
private Long uid;
@ApiModelProperty(value = "客服id")
private String agentId;
@ApiModelProperty(value = "客服名稱")
private String agentName;
}
// 實(shí)現(xiàn)類 MessageServiceImpl
package cn.huolala.llim.im.service.impl;
@Service
public class MessageServiceImpl extends ServiceImpl<IMessageMapper, Message> implements IMessageService {
@Autowired
private IMessageMapper iMessageMapper;
@Override
public List<Message> queryList(Message message, PageBO pager) {
// 多條件構(gòu)造器
QueryWrapper<Message> queryWrapper = new QueryWrapper<>();
queryWrapper
.select("uid", "agent_id", "agent_name") // 查詢指定字段
.isNotNull("uid").eq("uid", message.getUid());
queryWrapper.orderByDesc("created_at");
Page<Message> page = new Page<>(pager.getPage_no(), pager.getPage_size());
page = iMessageMapper.selectPage(page, queryWrapper);
return page.getRecords();
}
}
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;
@Test
public void stream() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Message message = new Message();
message.setUid(113074491521L);
PageBO page = new PageBO();
page.setPage_no(1);
page.setPage_size(10);
// 1. 對(duì)于 Message 列表去重
List<Message> messageList = iMessageService.queryList(message, page);
System.out.println("去重前=" + messageList);
// System.out.println("writeValueAsString = " + objectMapper.writeValueAsString(messageList));
messageList = messageList.stream().distinct().collect(Collectors.toList());
System.out.println("去重后=" + messageList);
// System.out.println("writeValueAsString = " + objectMapper.writeValueAsString(messageList));
messageList = messageList.stream().collect(
collectingAndThen(
toCollection(() -> new TreeSet<>(Comparator.comparing(Message::getAgentId))), ArrayList::new)
);
System.out.println("根據(jù)agentID去重后=" + messageList);
}
<== Columns: uid, agent_id, agent_name
<== Row: 113074491521, 107, wudy.yu2
<== Row: 113074491521, 107, wudy.yu
<== Row: 113074491521, 108, wudy.yu3
<== Row: 113074491521, 107, wudy.yu
去重前=[Message(uid=113074491521, agentId=107, agentName=wudy.yu2), Message(uid=113074491521, agentId=107, agentName=wudy.yu), Message(uid=113074491521, agentId=108, agentName=wudy.yu3), Message(uid=113074491521, agentId=107, agentName=wudy.yu)]
去重后=[Message(uid=113074491521, agentId=107, agentName=wudy.yu2), Message(uid=113074491521, agentId=107, agentName=wudy.yu), Message(uid=113074491521, agentId=108, agentName=wudy.yu3)]
根據(jù)agentID去重后=[Message(uid=113074491521, agentId=107, agentName=wudy.yu2), Message(uid=113074491521, agentId=108, agentName=wudy.yu3)]
3.根據(jù)List<Object>中 Object 某個(gè)屬性去重
// 見上例
messageList = messageList.stream().collect(
collectingAndThen(
toCollection(() -> new TreeSet<>(Comparator.comparing(Message::getAgentId))), ArrayList::new)
);
4.通過 filter() 方法
//我們首先創(chuàng)建一個(gè)方法作為 Stream.filter() 的參數(shù)阔拳,其返回類型為 Predicate,原理就是判斷一個(gè)元素能否加入到 Set 中去类嗤,代碼如下:
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor){
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t -> seen.add(keyExtractor.apply(t));
}
@Test
public void stream() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
Message message = new Message();
message.setUid(113074491521L);
PageBO page = new PageBO();
page.setPage_no(1);
page.setPage_size(10);
// 1. 對(duì)于 Message 列表去重
List<Message> messageList = iMessageService.queryList(message, page);
System.out.println("去重前=" + messageList);
messageList = messageList.stream().distinct().collect(Collectors.toList());
System.out.println("去重后=" + messageList);
//這里我們將 distinctByKey() 方法作為 filter() 的參數(shù)糊肠,過濾掉那些不能加入到 set 的元素
messageList = messageList.stream().filter(distinctByKey(Message::getAgentId)).collect(Collectors.toList());
System.out.println("根據(jù)agentID去重后=" + messageList);
}
<== Columns: uid, agent_id, agent_name
<== Row: 113074491521, 107, wudy.yu2
<== Row: 113074491521, 107, wudy.yu
<== Row: 113074491521, 108, wudy.yu3
<== Row: 113074491521, 107, wudy.yu
<== Total: 4
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1f6af096]
去重前=[Message(uid=113074491521, agentId=107, agentName=wudy.yu2), Message(uid=113074491521, agentId=107, agentName=wudy.yu), Message(uid=113074491521, agentId=108, agentName=wudy.yu3), Message(uid=113074491521, agentId=107, agentName=wudy.yu)]
去重后=[Message(uid=113074491521, agentId=107, agentName=wudy.yu2), Message(uid=113074491521, agentId=107, agentName=wudy.yu), Message(uid=113074491521, agentId=108, agentName=wudy.yu3)]
根據(jù)agentID去重后=[Message(uid=113074491521, agentId=107, agentName=wudy.yu2), Message(uid=113074491521, agentId=108, agentName=wudy.yu3)]
- 案例1
如果有一個(gè)需求,需要對(duì)數(shù)據(jù)庫(kù)查詢到的菜肴進(jìn)行一個(gè)處理:
篩選出卡路里小于400的菜肴
對(duì)篩選出的菜肴進(jìn)行一個(gè)排序
獲取排序后菜肴的名字
public class Dish {
private String name;
private boolean vegetarian;
private int calories;
private Type type;
// getter and setter
}
// java8之前的實(shí)現(xiàn)方式
private List<String> beforeJava7(List<Dish> dishList) {
List<Dish> lowCaloricDishes = new ArrayList<>();
//1.篩選出卡路里小于400的菜肴
for (Dish dish : dishList) {
if (dish.getCalories() < 400) {
lowCaloricDishes.add(dish);
}
}
//2.對(duì)篩選出的菜肴進(jìn)行排序
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
@Override
public int compare(Dish o1, Dish o2) {
return Integer.compare(o1.getCalories(), o2.getCalories());
}
});
//3.獲取排序后菜肴的名字
List<String> lowCaloricDishesName = new ArrayList<>();
for (Dish d : lowCaloricDishes) {
lowCaloricDishesName.add(d.getName());
}
return lowCaloricDishesName;
}
// java8之后的實(shí)現(xiàn)方式
private List<String> afterJava8(List<Dish> dishList) {
return dishList.stream()
.filter(d -> d.getCalories() < 400) //篩選出卡路里小于400的菜肴
.sorted(comparing(Dish::getCalories)) //根據(jù)卡路里進(jìn)行排序
.map(Dish::getName) //提取菜肴名稱
.collect(Collectors.toList()); //轉(zhuǎn)換為L(zhǎng)ist
}
高高興興寫完需求這時(shí)候又有新需求了遗锣,新需求如下:
對(duì)數(shù)據(jù)庫(kù)查詢到的菜肴根據(jù)菜肴種類進(jìn)行分類货裹,返回一個(gè)Map<Type, List<Dish>>的結(jié)果
這要是放在jdk8之前肯定會(huì)頭皮發(fā)麻
// java8之前的實(shí)現(xiàn)方式
private static Map<Type, List<Dish>> beforeJdk8(List<Dish> dishList) {
Map<Type, List<Dish>> result = new HashMap<>();
for (Dish dish : dishList) {
//不存在則初始化
if (result.get(dish.getType())==null) {
List<Dish> dishes = new ArrayList<>();
dishes.add(dish);
result.put(dish.getType(), dishes);
} else {
//存在則追加
result.get(dish.getType()).add(dish);
}
}
return result;
}
// java8的實(shí)現(xiàn)方式
private static Map<Type, List<Dish>> afterJdk8(List<Dish> dishList) {
return dishList.stream().collect(groupingBy(Dish::getType));
}
一、什么是流精偿?
流是從支持?jǐn)?shù)據(jù)處理操作的源生成的元素序列弧圆,源可以是數(shù)組、文件还最、集合墓阀、函數(shù)。流不是集合元素拓轻,它不是數(shù)據(jù)結(jié)構(gòu)并不保存數(shù)據(jù)斯撮,它的主要目的在于計(jì)算
二、如何生成流
生成流的方式主要有五種:
// 2.1>通過集合生成扶叉,應(yīng)用中最常用的一種
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream();
// 2.2>通過集合的stream方法生成流
// 2.2.1> 通過數(shù)組生成
int[] intArr = new int[]{1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(intArr);
/*
通過Arrays.stream方法生成流勿锅,并且該方法生成的流是數(shù)值流【即IntStream】而不是Stream<Integer>。
補(bǔ)充一點(diǎn)使用數(shù)值流可以避免計(jì)算過程中拆箱裝箱枣氧,提高性能溢十。Stream API提供了mapToInt、mapToDouble达吞、mapToLong三種方式將對(duì)象流
【即Stream<T>】轉(zhuǎn)換成對(duì)應(yīng)的數(shù)值流张弛,同時(shí)提供了boxed方法將數(shù)值流轉(zhuǎn)換為對(duì)象流
*/
// 2.2.2> 通過值生成
// 通過Stream的of方法生成流,通過Stream的empty方法可以生成一個(gè)空流
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
// 2.3> 通過文件生成
// 通過Files.line方法得到一個(gè)流,并且得到的每個(gè)流是給定文件中的一行
Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())
//2.4> 通過函數(shù)生成
//提供了iterate和generate兩個(gè)靜態(tài)方法從函數(shù)中生成流
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
// iterate方法接受兩個(gè)參數(shù)吞鸭,第一個(gè)為初始化值寺董,第二個(gè)為進(jìn)行的函數(shù)操作,因?yàn)閕terator生成的流為無(wú)限流刻剥,通過limit方法對(duì)流進(jìn)行了截?cái)嗾诳В簧?個(gè)偶數(shù)
Stream<Double> stream = Stream.generate(Math::random).limit(5);
//generate方法接受一個(gè)參數(shù),方法參數(shù)類型為Supplier<T>造虏,
//由它為流提供值御吞。generate生成的流也是無(wú)限流,因此通過limit對(duì)流進(jìn)行了截?cái)?
- 流的操作類型
流的操作類型主要分為兩種:
1.中間操作 一個(gè)流可以后面跟隨零個(gè)或多個(gè)中間操作漓藕。其目的主要是打開流陶珠,做出某種程度的數(shù)據(jù)映射/過濾,然后返回一個(gè)新的流撵术,交給下一個(gè)操作使用背率。這類操作都是惰性化的,僅僅調(diào)用到這類方法嫩与,并沒有真正開始流的遍歷,真正的遍歷需等到終端操作時(shí)交排,常見的中間操作有下面即將介紹的filter划滋、map等
2.終端操作 一個(gè)流有且只能有一個(gè)終端操作,當(dāng)這個(gè)操作執(zhí)行后埃篓,流就被關(guān)閉了处坪,無(wú)法再被操作,因此一個(gè)流只能被遍歷一次架专,若想在遍歷需要通過源數(shù)據(jù)在生成流同窘。終端操作的執(zhí)行,才會(huì)真正開始流的遍歷部脚。如下面即將介紹的count想邦、collect等
參考資料: https://mp.weixin.qq.com/s?__biz=MzUxOTc4NjEyMw==&mid=2247506967&idx=2&sn=c9c4b190331dc53faf77f8185e9fd534&chksm=f9f6c7f3ce814ee59109d3f2496eae5feaa46ef09fbc098e2c0d02be591fdeb043a3389c0cbc&mpshare=1&scene=23&srcid=0427RZolHIHFZHAY2n3NoRkE&sharer_sharetime=1619505638932&sharer_shareid=f770d25bc57f1c2f9159f85750f854dc#rd
- Stream對(duì)列表去重的幾種方法
- 1.Stream的distinct方法
distinct()
是Java 8 中Stream
提供的方法,返回的是由該流中不同元素組成的流委刘。distinct()使用hashCode()
和eqauls()
方法來(lái)獲取不同的元素丧没。
因此,需要去重的類必須實(shí)現(xiàn)hashCode()
和equals()
方法锡移。換句話講呕童,我們可以通過重寫定制的hashCode()
和equals()
方法來(lái)達(dá)到某些特殊需求的去重。
distinct()
方法聲明如下:- 1.1 對(duì)String列表去重:
因?yàn)?String 類已經(jīng)覆寫了 equals() 和 hashCode() 方法淆珊,所以可以去重成功
@Test
public void distinct(){
List<String> stringList = new ArrayList<String>(){
{
add("A");
add("B");
add("C");
add("A");
}
};
System.out.println("去重前:");
for (String s : stringList){
System.out.println(s);
}
stringList = stringList.stream().distinct().collect(Collectors.toList());
System.out.println("去重后:");
for (String s : stringList){
System.out.println(s);
}
}
去重前:
A
B
C
A
去重后:
A
B
C
- 1.2 對(duì)實(shí)體類列表的去重
注:代碼中我們使用了Lombok
插件的@Data
注解夺饲,可自動(dòng)覆寫equals()
以及hashCode()
方法
@Test
public void distinct() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
List<Student> sList = new ArrayList<>();
Student s1 = Student.builder().stuNo("001").name("wudy").build();
Student s2 = Student.builder().stuNo("002").name("peter").build();
Student s3 = Student.builder().stuNo("001").name("wudy").build();
sList.add(s1);
sList.add(s2);
sList.add(s3);
System.out.printf("去重前:" + mapper.writeValueAsString(sList));
sList = sList.stream().distinct().collect(Collectors.toList());
System.out.printf("去重后:" + mapper.writeValueAsString(sList));
}
去重前:[{"stuNo":"001","name":"Tom"},{"stuNo":"002","name":"Mike"},{"stuNo":"001","name":"Tom"}]
去重后:[{"stuNo":"001","name":"Tom"},{"stuNo":"002","name":"Mike"}]
- 1.3 根據(jù)List<Object>中Object某個(gè)屬性去重
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;
@Test
public void test21() throws JsonProcessingException {
List<Student> list = new ArrayList<>();
Student s1 = Student.builder().stuNo("001").stuName("wudy").build();
Student s2 = Student.builder().stuNo("002").stuName("peter").build();
Student s3 = Student.builder().stuNo("001").stuName("wudy").build();
list.add(s1);
list.add(s2);
list.add(s3);
ObjectMapper mapper = new ObjectMapper();
System.out.printf("去重前=" + mapper.writeValueAsString(list));
list = list.stream().collect(
collectingAndThen(
toCollection(() -> new TreeSet<>(Comparator.comparing(Student::getStuName))), ArrayList::new)
);
System.out.printf("去重后=" + mapper.writeValueAsString(list));
}
去重前=[{"stuName":"wudy","stuNo":"001"},{"stuName":"peter","stuNo":"002"},{"stuName":"wudy","stuNo":"001"}]
去重后=[{"stuName":"peter","stuNo":"002"},{"stuName":"wudy","stuNo":"001"}]
- 1.4 通過filter方法去重
@Test
public void test21() throws JsonProcessingException {
List<Student> list = new ArrayList<>();
Student s1 = Student.builder().stuNo("001").stuName("wudy").build();
Student s2 = Student.builder().stuNo("002").stuName("peter").build();
Student s3 = Student.builder().stuNo("001").stuName("wudy").build();
list.add(s1);
list.add(s2);
list.add(s3);
ObjectMapper mapper = new ObjectMapper();
System.out.printf("去重前=" + mapper.writeValueAsString(list));
// 將 distinctByKey() 方法作為 filter() 的參數(shù),過濾掉那些不能加入到 set 的元素
list = list.stream().filter(dictinctByKey(Student::getStuName)).collect(Collectors.toList());
System.out.printf("去重后=" + mapper.writeValueAsString(list));
}
private static <T> Predicate<T> dictinctByKey(Function<? super T, ?> keyExtractor){
Set<Object> keys = ConcurrentHashMap.newKeySet();
return t -> keys.add(keyExtractor.apply(t));
}
去重前=[{"stuName":"wudy","stuNo":"001"},{"stuName":"peter","stuNo":"002"},{"stuName":"wudy","stuNo":"001"}]
去重后=[{"stuName":"wudy","stuNo":"001"},{"stuName":"peter","stuNo":"002"}]
Stream圖解
Stream
將要處理的元素集合看作一種流,在流的過程中往声,借助Stream API對(duì)流中的元素進(jìn)行操作擂找,比如:篩選、排序烁挟、聚合等
Stream可以由數(shù)組或集合創(chuàng)建婴洼,對(duì)流的操作分為兩種:
中間操作,每次返回一個(gè)新的流撼嗓,可以有多個(gè)柬采。
終端操作,每個(gè)流只能進(jìn)行一次終端操作且警,終端操作結(jié)束后流無(wú)法再次使用粉捻。終端操作會(huì)產(chǎn)生一個(gè)新的集合或值。
另外斑芜,Stream有幾個(gè)特性:
- stream不存儲(chǔ)數(shù)據(jù)肩刃,而是按照特定的規(guī)則對(duì)數(shù)據(jù)進(jìn)行計(jì)算,一般會(huì)輸出結(jié)果杏头。
- stream不會(huì)改變數(shù)據(jù)源盈包,通常情況下會(huì)產(chǎn)生一個(gè)新的集合或一個(gè)值。
- stream具有延遲執(zhí)行特性醇王,只有調(diào)用終端操作時(shí)呢燥,中間操作才會(huì)執(zhí)行。
- 1.Stream創(chuàng)建:
1.1> Stream可以通過集合數(shù)組創(chuàng)建,通過 java.util.Collection.stream() 方法用集合創(chuàng)建流
List<String> list = Arrays.asList("a", "b", "c");
// 創(chuàng)建一個(gè)順序流
Stream<String> stream = list.stream();
// 創(chuàng)建一個(gè)并行流
Stream<String> parallelStream = list.parallelStream();
1.2> 使用java.util.Arrays.stream(T[] array)方法用數(shù)組創(chuàng)建流
int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);
1.3> 使用Stream的靜態(tài)方法:of()寓娩、iterate()叛氨、generate()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::println); // 0 2 4 6 8 10
Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);
stream
和parallelStream
的簡(jiǎn)單區(qū)分: stream
是順序流
,由主線程按順序?qū)α鲌?zhí)行操作棘伴,而parallelStream
是并行流
寞埠,內(nèi)部以多線程
并行執(zhí)行的方式對(duì)流進(jìn)行操作,但前提是流中的數(shù)據(jù)處理沒有順序要求焊夸。例如篩選集合中的奇數(shù)仁连,兩者的處理不同之處
Stream的案例使用
https://mp.weixin.qq.com/s?__biz=MzI3ODcxMzQzMw==&mid=2247539728&idx=2&sn=a3397ae7db0d7c13f21ae4d650b237ec&chksm=eb50d926dc275030a3ac919708b53af1a4bd339bce6e4f1351fd1eb88f9a5c2fdecdd2c6b02b&mpshare=1&scene=23&srcid=08216nSfljTSZcpp5Nql4z7m&sharer_sharetime=1629510377638&sharer_shareid=f770d25bc57f1c2f9159f85750f854dc%23rd
Stream簡(jiǎn)單案例
https://mp.weixin.qq.com/s?__biz=MzUxOTc4NjEyMw==&mid=2247533024&idx=3&sn=393d7130c992ab67d5ce2dde66b165dc&chksm=f9f65a04ce81d312c3edc482d796f68b250e9bca943c77ce10e36e16e6a3c0850f10d58a7c24&mpshare=1&scene=23&srcid=0521nfdgr7cnkMhqRtFFGqz2&sharer_sharetime=1653147530521&sharer_shareid=7fec9c1809ccb850bfdebba7d4f7a81e%23rd
// 1.list => 數(shù)組
List<Integer> arrayList = new ArrayList<Integer>(Arrays.asList(1,3,5,7,9));
// int[] arr = arrayList.stream().mapToInt((item) -> Integer.valueOf(item)).toArray();
int[] arr = arrayList.stream().mapToInt(Integer::intValue).toArray();
for (int i=0;i<arr.length;i++) {
System.out.println("item=" + arr[i]);
}
// 2.數(shù)組 => list
int[] arr2 = {2,4,6,8};
List<Integer> list = Arrays.stream(arr2).boxed().collect(Collectors.toList());
for (Integer item: list) {
System.out.println("item="+ item);
}
// 3.統(tǒng)計(jì)數(shù)組中的元素
String[] arr3 = {"a", "c", "a", "b", "d", "c"};
// 第一個(gè)參數(shù)代表將arr中的每一個(gè)元素作為Map中的key,第二個(gè)參數(shù)代表每一個(gè)key所對(duì)應(yīng)的value淳地,在這里每一個(gè)元素都對(duì)應(yīng)個(gè)數(shù)1怖糊,第三個(gè)參數(shù)代表,如果存在相同的key颇象,該如何進(jìn)行合并伍伤,這里通過使用Integer::sum,代表將具有相同key的元素進(jìn)行合并時(shí)遣钳,其value進(jìn)行相加
Stream.of(arr3).collect(Collectors.toMap(k -> k, k -> 1, Integer::sum))
.forEach((k, v) -> System.out.println(k + ": " + v));
// 4.基本數(shù)據(jù)類型的數(shù)組自定義排序
int[] arr4 = {1, 5, 9, 7, 2, 3, 7, -1, 0, 3};
int[] arr5 = IntStream.of(arr4).boxed()
.sorted(Comparator.reverseOrder())
.mapToInt(Integer::intValue)
.toArray();
for (Integer item:arr5) {
System.out.println("item5="+ item);
}
// 5.統(tǒng)計(jì)數(shù)組中前 k 個(gè)個(gè)高頻元素
int[] arr6 = {1,3,2,55,33,3,6,1,55,3,2,0,98,3,1,41,52,2,1,3};
int[] arr7 = Arrays.stream(arr6)
.boxed()
.collect(Collectors.toMap(e -> e, e -> 1, Integer::sum))
.entrySet()
.stream()
.sorted((m1, m2) -> m2.getValue() - m1.getValue())
.limit(2)
.mapToInt(Map.Entry::getKey)
.toArray();
for (Integer item:arr7) {
System.out.println("item7="+ item);
}