JDK8漫談——集合更強大

解決什麼問題

  • 集合計算不足
  • 解決重複代碼

背後思想

  • 管道
  • 封裝
  • 數據處理

內容說明

是什麼

  • 計算擔當。集合用於數據存儲,流用於數據計算,不會修改原始數據
  • 內置循環。高級迭代器,內置循環和計算
  • 單向。數據只能遍歷一次,遍歷過一次後即用盡了,像水流過,不可往復

生命週期

Stream像操做SQL同樣處理數據,因此很概念很是相近,能夠對比着理解 。html

建立

分爲靜態建立和實例建立,不一樣的方法適用於不一樣場景。目的都是爲了轉成流java

/**
     * 初始
     * <p>
     *     適用於數組和簡單的數據轉爲stream
     * </p>
     */
    @Test
    public void test_static_of() {
        Stream.of(Person.builder().name("name").age(1));

        /**
         * 打印的是list的元素
         */
        Stream.of(newArray()).forEach(System.out::println);
        /**
         * 打印的是list的toString,因此集合不使用該方法
         */
        Stream.of(newList()).forEach(System.out::println);
    }

    /**
     * 範圍
     * <p>
     *     適用於整數迭代
     * </p>
     */
    @Test
    public void test_static_range(){
        /**
         * 不包含10
         */
        IntStream.range(1,10).forEach(System.out::println);
        /**
         * 包含10
         */
        IntStream.rangeClosed(1,10).forEach(System.out::println);
    }

    /**
     * 生成
     * <p>
     *     適用於批量生成而且賦值
     *     好比生成id或者uuid
     * </p>
     */
    @Test
    public void test_static_generate() {
        Stream.generate(UUID::randomUUID)
                .limit(10)
                .forEach(System.out::println);

        Stream.generate(()->Math.random()*1000)
                .limit(10)
                .forEach(System.out::println);

    }

    @Test
    public void test_static_iterate() {
        Stream.iterate(10, index -> {
            Person.builder().name("name").age(index);
            return --index;
        }).limit(10).forEach(System.out::println);
    }

    /**
     * 集合
     */
    @Test
    public void test_instance_collection() {
        /**
         * 打印的是list的元素
         */
        newList().stream().forEach(System.out::println);
    }

    /**
     * 正則
     */
    @Test
    public  void test_regex() {
        String sentence = "Program creek is a Java site.";
        Pattern.compile("\\W").splitAsStream(sentence).forEach(System.out::println);
    }

    /**
     * 文件
     * @throws IOException
     */
    @Test
    public void test_file() throws IOException {
        String fileName = "c://lines.txt";
        Files.lines(Paths.get(fileName)).forEach(System.out::println);
    }

    private List<String> newList() {
        List<String> list = new ArrayList<>();
        list.add("lisi");
        return list;
    }


    private String[] newArray() {
        return new String[]{"sili", "wangwu"};
    }

轉換

把一個流轉爲另外一個流,能夠不斷的轉換。包括:過濾,轉換,限制,排序,去重。經過不斷的轉換最終獲取目標數據數據庫

private Stream<Person> newStream(){
        List<Person> list = new ArrayList<>();
        list.add(Person.builder().name("zhangsan").age(12).build());
        list.add(Person.builder().name("zhangsan").age(12).build());
        list.add(Person.builder().name("lisi").age(18).build());
        list.add(Person.builder().name("wangwu").age(25).build());
        return list.stream();
    }

    /**
     * 過濾
     * <p>
     *     過濾不符合條件的數據。
     *     好比說:
     *     <ul>
     *         <li>結合數據庫查詢,在java裏過濾數據</li>
     *         <li>過濾掉npe</li>
     *         <li>過濾不符合業務數據</li>
     *     </ul>
     * </p>
     */
    @Test
    public void test_filter(){
        newStream()
                .filter(person->person.getAge()>=18)
                .forEach(System.out::println);

    }

    /**
     * 類型轉換
     * <p>
     *     用於快速轉換數據類型
     *     好比說:
     *     <ul>
     *         <li>根據實例快速轉出id集合</li>
     *     </ul>
     * </p>
     */
    @Test
    public void test_mapper(){
        newStream()
                .map(Person::getId)
                .forEach(System.out::println);
    }

    /**
     * 限制
     * <p>
     *     取前幾個
     * </p>
     */
    @Test
    public void test_limit(){
        newStream()
                .limit(2)
                .forEach(System.out::println);
    }

    /**
     * 排序
     */
    @Test
    public void test_sorted(){
        newStream()
                .sorted((p1,p2)->{
                    if(p1.getAge()>p2.getAge()){
                        return 1;
                    }else if (p1.getAge()<p2.getAge()){
                        return -1;
                    }else{
                        return 0;
                    }
                })
                .forEach(System.out::println);
    }
    /**
     * 排序
     */
    @Test
    public void test_sorted_comparator() {
        newStream()
                .sorted(Comparator
                        //排序
                        .comparing(Person::getAge)
                        //倒序
                        .reversed()
                        .thenComparing(Person::getName)
                )
                .forEach(System.out::println);
    }

    /**
     * 去重
     */
    @Test
    public void test_distinct(){
        newStream()
                .distinct()
                .forEach(System.out::println);
    }

    /**
     * 綜合使用
     * <p>
     *     經過這些計算能夠快速找出期待的數據,並且還不須要不斷的添加遍歷和破壞代碼可讀性
     * </p>
     */
    @Test
    public void test_combine(){
        newStream()
                .filter(person->null==person)
                .distinct()
                .filter(person -> person.getAge()>=18)
                .map(Person::getAge)
                .sorted((age1,age2)->{
                    if(age1>age2){
                        return 1;
                    }else if (age1<age2){
                        return -1;
                    }else{
                        return 0;
                    }
                })
                .limit(1)
                .forEach(System.out::println);
    }
  • 無須重複循環
  • 添加需求時,無須破壞式的修改內容

聚合

一個流只能有一個停止動做。停止後流就不能使用。包括:查找,匹配,循環,統計,類型轉換,分組。數組

private Stream<Person> newStream() {
        List<Person> list = new ArrayList<>();
        list.add(Person.builder().name("zhangsan").age(12).build());
        list.add(Person.builder().name("zhangsan").age(12).build());
        list.add(Person.builder().name("lisi").age(18).build());
        list.add(Person.builder().name("wangwu").age(25).build());
        return list.stream();
    }

    /**
     * 查找
     *
     */
    @Test
    public void test_find() {
        /**
         * 返回第一個值
         */
        Optional<Person> first = newStream().findFirst();
        Assert.assertEquals(first.get(),newStream().collect(toList()).get(0));
        /**
         * 返回隨機值
         */
        Optional<Person> any = newStream().findAny();
        System.out.println(any.get());
    }

    /**
     * 匹配
     */
    @Test
    public void test_match() {
        Assert.assertTrue(newStream().anyMatch(person -> person.getAge() == 18));
        Assert.assertFalse(newStream().allMatch(person -> person.getAge() == 18));
        Assert.assertFalse(newStream().noneMatch(person -> person.getAge() == 18));
    }

    /**
     * 統計
     *
     */
    @Test
    public void test_stat_max() {
        Optional<Person> maxAge = newStream().max((p1, p2) -> {
            if (p1.getAge() > p2.getAge()) {
                return 1;
            } else if (p1.getAge() < p2.getAge()) {
                return -1;
            } else {
                return 0;
            }
        });
        System.out.println(maxAge);
    }

    /**
     * 個數
     */
    @Test
    public void test_stat_count() {
        long count = newStream().count();
        System.out.println(count);
    }

    /**
     * 循環
     */
    @Test
    public void test_forEach() {
        newStream().forEach(System.out::println);
    }

    /**
     * 歸併
     */
    @Test
    public void test_reduce() {
        /**
         * 第一個參數是上次結果
         * 第二個參數是元素
         */
        Optional<Integer> totalAge = newStream()
                .map(Person::getAge)
                .reduce((result, age) -> result + age);
        System.out.println(totalAge.get());

        /**
         * 第一個參數是初始值
         * 第二個參數是上次結果
         * 第三個參數是元素
         */
        Integer totalAgeWithInit = newStream()
                .map(Person::getAge)
                .reduce(10, (result, age) -> result + age);
        System.out.println(totalAgeWithInit);
    }

    /**
     * 轉爲數組
     *
     */
    @Test
    public void test_toArray(){
        newStream().toArray();
    }

    /**
     * 轉爲collection
     */
    @Test
    public void test_toCollection(){
        newStream().collect(toList());
        newStream().collect(toSet());
    }
    /**
     * 轉爲map
     */
    @Test
    public void test_toMap(){
        /**
         * 第一個參數是key
         * 第二個參數是value
         *
         * <p>
         *     若是有重複主鍵時會報錯。
         *     若是Key爲null會報錯
         * </p>
         */
        //newStream().collect(toMap(Person::getId,person->person));
        /**
         * 第三個參數是若是key重複的時候取哪一個值
         */
        newStream().collect(toMap(Person::getId,person->person,(oldValue,newValue)->oldValue));
        newStream().collect(toMap(Person::getId,person->person,(oldValue,newValue)->newValue));
        /**
         * 返回具體的map實例類型
         */
        newStream().collect(toMap(Person::getId,person->person,(oldValue,newValue)->oldValue,HashMap::new));
        newStream().collect(toMap(Person::getId,person->person,(oldValue,newValue)->oldValue,LinkedHashMap::new));
    }

    /**
     * 經過分組轉爲Map
     *
     * <p>
     *     分組完,通常會對元素進行聚合
     * </p>
     */
    @Test
    public void test_toMap_group(){
        /**
         * 不存在重複key報錯的問題
         */
        Map<Long, List<Person>> listMap = newStream().collect(groupingBy(Person::getId));
        /**
         * 可是返回值是double
         */
        Map<Long, Double> averageAgeMap = newStream().collect(
                groupingBy(Person::getId,
                        averagingLong(Person::getAge))
        );
        /**
         * 找出最大年齡對應的Map
         */
        Map<String, Optional<Person>> maxAgeMap = newStream().collect(
                groupingBy(Person::getName,
                        maxBy(Comparator.comparing(Person::getAge)))
        );
    }

實踐

可維護性

  • 點號換行,提高可閱讀性
  • 不要超過3行
  • 職責單一
@Test
    public void test_format() {
        newStream()
                .filter(person -> null == person)
                .distinct()
                .filter(person -> person.getAge() >= 18)
                .map(Person::getAge)
                .sorted((age1, age2) -> {
                    if (age1 > age2) {
                        return 1;
                    } else if (age1 < age2) {
                        return -1;
                    } else {
                        return 0;
                    }
                })
                .limit(1)
                .forEach(System.out::println);


        newStream()
                .filter(person -> null == person)
                .distinct()
                .filter(person -> person.getAge() >= 18)
                .map(Person::getAge)
                //上面排序超過三行,不容易閱讀
                .sorted(Comparator.comparing(Long::valueOf))
                .limit(1)
                .forEach(System.out::println);
    }

    @Test
    public void test_single() {
        newStream()
                .filter(person -> {
                    if(null != person){
                        return true;
                    }
                    if(person.getAge() > 18){
                        return true;
                    }
                    if(person.getName().startsWith("hello")){
                        return true;
                    }
                    return false;
                })
        ;

        newStream()
                .filter(person -> null != person)
                .filter(person -> person.getAge() > 18)
                .filter(person -> person.getName().startsWith("hello"))
        ;
    }
  • 語義更明確,不須要面向每個過程。
  • 代碼更簡潔,修改更方便。
@Test
    public void test_forEach() {
        List<Person> persons = new ArrayList<>();
        List<Person> adultPersons = new ArrayList<>();
        for (Person person : persons) {
            if (person.getAge() > 18) {
                adultPersons.add(person);
            }
        }

        List<Person> adultPersons2 = persons.stream()
                .filter(person -> person.getAge() > 18)
                .collect(Collectors.toList());
    }

陷阱

@Test
    public void test_forEach(){
        newStream().forEach(person -> {
            // 不要在forEach裏作過濾的行爲
            if(person.getAge()>18){
                /**
                 * 這裏的return就至關於for裏的continue,後面的例子仍是會繼續的。
                 * 沒法實現break的效果,必定會把全部元素走完
                 */
                return ;
            }
            System.out.println(person);
        });
        
        newStream()
                .filter(person -> person.getAge()>18)
                .forEach(System.out::println);
    }
  • 若是有停止的需求,不要使用
  • 只作動做,不要作過濾
  • 不能用於須要索引的情景
  • 不能用於變動集合的情景
  • 使用時須要注意NPE

思考

  • 類型轉換比較冗長,是否經過工具類封裝或者經過重寫這些子類來而且經過默認方法來進一步內置行爲
  • stream和interate的區別
  • stream併發的使用

加入咱們

誠邀,有夢想有激情有實力的小夥伴一塊兒加入咱們,改變世界。下面是JD的詳情信息併發

阿里-菜鳥國際-出口大團隊招新啦app

相關文章
相關標籤/搜索