java之Stream的常見用法

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è)管道
516E226E-0FFF-43E9-9283-779D9491EB38.png
  • 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

如下圖:


B6AC3DC1-1866-434E-AF24-A4A6FA232579.png

-案例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圖解

image.png

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);

streamparallelStream的簡(jiǎn)單區(qū)分: stream順序流,由主線程按順序?qū)α鲌?zhí)行操作棘伴,而parallelStream并行流寞埠,內(nèi)部以多線程并行執(zhí)行的方式對(duì)流進(jìn)行操作,但前提是流中的數(shù)據(jù)處理沒有順序要求焊夸。例如篩選集合中的奇數(shù)仁连,兩者的處理不同之處

image.png
  • 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);
        }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末扰魂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌劝评,老刑警劉巖姐直,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蒋畜,居然都是意外死亡声畏,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門姻成,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)插龄,“玉大人,你說(shuō)我怎么就攤上這事科展【危” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵才睹,是天一觀的道長(zhǎng)徘跪。 經(jīng)常有香客問我,道長(zhǎng)琅攘,這世上最難降的妖魔是什么垮庐? 我笑而不...
    開封第一講書人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮坞琴,結(jié)果婚禮上突硝,老公的妹妹穿的比我還像新娘。我一直安慰自己置济,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開白布锋八。 她就那樣靜靜地躺著浙于,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挟纱。 梳的紋絲不亂的頭發(fā)上羞酗,一...
    開封第一講書人閱讀 50,021評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音紊服,去河邊找鬼檀轨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛欺嗤,可吹牛的內(nèi)容都是我干的参萄。 我是一名探鬼主播,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼煎饼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼讹挎!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤筒溃,失蹤者是張志新(化名)和其女友劉穎马篮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怜奖,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浑测,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了歪玲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片迁央。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖读慎,靈堂內(nèi)的尸體忽然破棺而出漱贱,到底是詐尸還是另有隱情,我是刑警寧澤夭委,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布幅狮,位于F島的核電站,受9級(jí)特大地震影響株灸,放射性物質(zhì)發(fā)生泄漏崇摄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一慌烧、第九天 我趴在偏房一處隱蔽的房頂上張望逐抑。 院中可真熱鬧,春花似錦屹蚊、人聲如沸厕氨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)命斧。三九已至,卻和暖如春嘱兼,著一層夾襖步出監(jiān)牢的瞬間国葬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工芹壕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汇四,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓踢涌,卻偏偏與公主長(zhǎng)得像通孽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子斯嚎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容