1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        淺談Java 8 API增強(qiáng)

        共 23138字,需瀏覽 47分鐘

         ·

        2021-06-23 10:27

        這里針對(duì)Java 8在API方面的增強(qiáng)及相關(guān)使用方式做一些簡(jiǎn)單的介紹

        abstract.png

        Collection

        removeIf方法

        眾所周知,對(duì)于List、Set等集合而言,如果期望刪除某些元素,其實(shí)是一件非常麻煩的事情。例如下面的示例,刪除列表中長(zhǎng)度大于2的元素

        @Test
        public void test1() {
            List<String> list = new LinkedList<>();
            list.add("乒乓球");
            list.add("足球");
            list.add("羽毛球");
            list.add("籃球");

            list.forEach( e- > {
                // 移除長(zhǎng)度大于2的元素
                if( e.length()>2 ) {
                    list.remove(e);
                }
            });

            System.out.println("list: " + list);
        }

        結(jié)果顯然易見,如下所示,刪除失敗。原因自然是不言自明。在迭代器遍歷過(guò)程中,通過(guò)list.remove方法進(jìn)行刪除是不允許的。除非我們顯式使用迭代器進(jìn)行遍歷、刪除,顯然這樣非常繁瑣

        figure 1.jpeg

        為此Java8在Collection中提供removeIf默認(rèn)方法,其接收一個(gè)謂詞參數(shù)。大大方便了我們對(duì)集合進(jìn)行刪除的操作,示例如下所示

        @Test
        public void test2() {
            List<String> list = new LinkedList<>();
            list.add("乒乓球");
            list.add("足球");
            list.add("羽毛球");
            list.add("籃球");

            // 移除長(zhǎng)度大于2的元素
            list.removeIf( e -> e.length()>2 );
            System.out.println("list: " + list);
        }

        測(cè)試結(jié)果如下,符合預(yù)期

        figure 2.jpeg

        Map

        計(jì)算模式

        在Map中可以根據(jù)Key存在與否的情況,按條件執(zhí)行相關(guān)操作并將計(jì)算結(jié)果作為Value存儲(chǔ)到Map中。具體有以下方法:

        • compute:使用指定的Key計(jì)算新值,并將計(jì)算結(jié)果作為Value存儲(chǔ)到Map中
        • computeIfAbsent:如果指定Key在Map中沒有對(duì)應(yīng)的值(Key不存在 或 其值為null), 則計(jì)算該Key的新值并存儲(chǔ)到Map中
        • computeIfPresent:如果指定Key在Map中有對(duì)應(yīng)的值(Key存在 且 其值不為null),則計(jì)算該Key的新值并存儲(chǔ)到Map中

        compute方法

        比如下面的代碼,是一個(gè)統(tǒng)計(jì)單詞次數(shù)的方法

        public void test1() {
            // 每個(gè)單詞出現(xiàn)的次數(shù), key: 單詞; value: 次數(shù)
            Map<String, Integer> map = new HashMap<>();

            String doc = "I am Aaron I like Aaron";
            String[] words = doc.split(" ");

            for(String word : words) {
                Integer count = map.get(word);
                if( count==null ) {
                    count = 0;
                }
                count++;
                map.put(word, count);
            }

            System.out.println("map : " + map);
        }

        如果采用compute方法則可以大大簡(jiǎn)化代碼,如下所示。利用Key及其Value進(jìn)行計(jì)算,并將計(jì)算結(jié)果作為該Key的新值存儲(chǔ)到Map中

        @Test
        public void test2() {
            // 每個(gè)單詞出現(xiàn)的次數(shù), key: 單詞; value: 次數(shù)
            Map<String, Integer> map = new HashMap<>();

            String doc = "I am Aaron I like Aaron";
            String[] words = doc.split(" ");

            for(String word : words) {
                map.compute( word, (key, oldValue) ->{
                    if( oldValue==null ) {
                        oldValue = 0;
                    }
                    oldValue++;
                    return oldValue;
                });
            }

            System.out.println("<Test 2> map : " + map);
        }

        兩個(gè)方法測(cè)試結(jié)果如下,符合預(yù)期

        figure 3.jpeg

        computeIfAbsent方法

        下面是一個(gè)在Map中保存各人所喜歡的書單的方法。每次在向value中添加書時(shí),都需要判斷該value是否為空,非常繁瑣

        @Test
        public void test1() {
            // 每個(gè)人喜歡的書單, key: 人名; value: 書名列表
            Map<String, List<String>> map = new HashMap<>();

            // 場(chǎng)景: 給Amy喜歡的書單添加《老人與海》
            String name = "Amy";
            String bookName = "老人與海";
            List list = map.get(name);
            if( list==null ) {
                list = new LinkedList<>();
                map.put(name, list);
            }

            list.add(bookName);

            System.out.println("<Test 1> map : " + map);
        }

        幸運(yùn)的是,在有了computeIfAbsent方法后,我們實(shí)現(xiàn)起來(lái)就簡(jiǎn)潔很多

        @Test
        public void test2() {
            // 每個(gè)人喜歡的書單, key: 人名; value: 書名列表
            Map<String, List<String>> map = new HashMap<>();

            // 場(chǎng)景: 給Amy喜歡的書單添加《老人與?!?/span>
            String name = "Amy";
            String bookName = "老人與海";
            // 如果map中 key不存在 或 key所對(duì)應(yīng)的值為null
            // 則會(huì)通過(guò)第二個(gè)參數(shù) function 計(jì)算新value,并put到map中
            // 該方法最終會(huì)返回該key所對(duì)應(yīng)的value值
            List<String> list = map.computeIfAbsent( name, key -> new LinkedList<>() );

            list.add(bookName);

            System.out.println("<Test 2> map : " + map);
        }

        可以看到,computeIfAbsent方法非常適用于value為集合類型、且需要向該集合中添加元素的場(chǎng)景。測(cè)試結(jié)果如下,符合預(yù)期

        figure 4.jpeg

        與此同時(shí),computeIfAbsent方法也適用于緩存信息的場(chǎng)景。在下面的示例中,對(duì)于已有簽名數(shù)據(jù)作為Value的Key,顯然沒有必要重復(fù)計(jì)算、浪費(fèi)資源

        @Test
        public void test3() {

            Map<String, Integer> map = new HashMap<>();

            System.out.println("\n-------------------- Test 1: Start --------------------");
            map.computeIfAbsent("China"this::calcSign);
            System.out.println("-------------------- Test 1: End ----------------------");

            System.out.println("\n-------------------- Test 2: Start --------------------");
            map.computeIfAbsent("USA"this::calcSign);
            System.out.println("-------------------- Test 2: End ----------------------");

            System.out.println("\n-------------------- Test 3: Start --------------------");
            map.computeIfAbsent("China"this::calcSign);
            System.out.println("-------------------- Test 3: End ----------------------");

            System.out.println("\nmap: " + map);
        }

        /**
         * 計(jì)算簽名
         * @param str
         * @return
         */

        private Integer calcSign(String str) {
            System.out.println( "Start Calc Sign, str: " + str);
            Integer result = str.hashCode();
            return result;
        }

        測(cè)試結(jié)果如下,符合預(yù)期

        figure 5.jpeg

        computeIfPresent方法

        下面是一個(gè)在Map中保存各人所喜歡的書單的方法。每次從value中移除書時(shí),都需要判斷該value是否為空,非常繁瑣

        @Test
        public void test1() {
            // 每個(gè)人喜歡的書單, key: 人名; value: 書名列表
            Map<String, List<String>> map = new HashMap();
            // map數(shù)據(jù)初始化
            map.put("Amy"new ArrayList(Arrays.asList("資治通鑒""金瓶梅""山海經(jīng)")) );

            // 場(chǎng)景: Amy喜歡的書單中不能有《金瓶梅》
            String name = "Amy";
            String bookName = "金瓶梅";
            List<String> list = map.get(name);
            if( list!=null ) {
                list.remove( bookName );
            }

            System.out.println("<Test 1> map : " + map);
        }

        結(jié)果符合預(yù)期,如下所示

        figure 6.jpeg

        幸運(yùn)的是,在有了computeIfPresent方法后,我們實(shí)現(xiàn)起來(lái)就簡(jiǎn)潔很多

        @Test
        public void test2() {
            // 每個(gè)人喜歡的書單, key: 人名; value: 書名列表
            Map<String, List<String>> map = new HashMap<>();
            // map數(shù)據(jù)初始化
            map.put("Amy"new ArrayList(Arrays.asList("資治通鑒""金瓶梅""山海經(jīng)")) );
            map.put("Bob"new ArrayList(Arrays.asList("三年高考五年模擬")) );
            map.put("Tony"new ArrayList(Arrays.asList("21天入門理發(fā)")) );

            // 場(chǎng)景: Amy喜歡的書單中不能有《金瓶梅》
            String name = "Amy";
            String bookName1 = "金瓶梅";
            // 如果map中key所對(duì)應(yīng)的value不為null
            // 則會(huì)通過(guò)第二個(gè)參數(shù), 利用key、oldValue 計(jì)算newValue,并put到map中
            // 該方法最終會(huì)返回該key所對(duì)應(yīng)的value值
            map.computeIfPresent(name, (key, value) -> {
                value.remove( bookName1 );
                return value;
            } );

            // 場(chǎng)景: Bob喜歡的書單中不能有《三年高考五年模擬》
            name = "Bob";
            String bookName2 = "三年高考五年模擬";
            map.computeIfPresent(name, (key, value) -> {
                value.remove( bookName2 );
                return value;
            } );

            // 場(chǎng)景: 用戶Tony注銷了,不需要再保存其喜歡的書單
            name = "Tony";
            // 在computeIfPresent方法中, 如果該key計(jì)算的newValue為null, 則該映射會(huì)被移除
            map.computeIfPresent(name, (key, value) -> {
                List newValue = null;
                return newValue;
            } );

            System.out.println("<Test 2> map : " + map);
        }

        可以看到,computeIfPresent方法非常適用于value為集合類型、且需要從集合中移除元素的場(chǎng)景。測(cè)試結(jié)果如下,符合預(yù)期。值得一提的是在computeIfPresent方法中,如果該key計(jì)算的新值為null, 則該映射會(huì)被移除

        figure 7.jpeg

        merge方法

        Map接口提供的默認(rèn)方法merge,簽名如下。其第一、二個(gè)參數(shù)就是我們期望存儲(chǔ)到Map的key、newValue。但如果該key在map已經(jīng)存在且其值(這里記為oldValue)不為空,則就需要通過(guò) 第三個(gè)參數(shù)(BiFunction類型),其定義了合并oldValue、newValue這兩個(gè)值的計(jì)算規(guī)則

        merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction) {

        通過(guò)下面的例子,可以更好的幫助大家理解

        @Test
        public void test1() {

            Map<String, String> map = new HashMap<>();
            map.put("Aaron""籃球");

            map.merge("Bob""足球", (oldValue, newValue) -> oldValue+"&"+newValue );
            System.out.println("map 1: " + map);

            map.merge("Aaron""乒乓球", (oldValue, newValue) -> oldValue+"&"+newValue );
            System.out.println("map 2: " + map);
        }
        測(cè)試結(jié)果如下,符合預(yù)期

        figure 8.jpeg

        可以看到,由于merge方法對(duì)于欲插入的key是否在map中存在不為null的value,實(shí)際上是提供了兩種不同的計(jì)算路徑。故對(duì)于上文通過(guò)compute方法實(shí)現(xiàn)次數(shù)統(tǒng)計(jì)的例子而言,我們?nèi)绻褂胢erge方法實(shí)現(xiàn)會(huì)更加簡(jiǎn)單,示例代碼如下所示

        @Test
        public void test2() {
            // 每個(gè)單詞出現(xiàn)的次數(shù), key: 單詞; value: 次數(shù)
            Map<String, Integer> map = new HashMap<>();
            String doc = "can Aaron can Aaron like I can like Aaron can";
            String[] words = doc.split(" ");

            for(String word : words) {
                // 該單詞在map中value為null,說(shuō)明該單詞首次被統(tǒng)計(jì), 故直接記為1
                // 該單詞在map中value不為null,說(shuō)明該單詞非首次被統(tǒng)計(jì), 故使用原有次數(shù)自增1
                map.merge(word, 1, (oldValue, newValue)->oldValue+1 );
            }

            System.out.println("<Test 2> map : " + map);
        }

        測(cè)試結(jié)果如下,符合預(yù)期

        figure 9.jpeg

        Optional

        orElse與orElseGet

        二者都是用于在option實(shí)例中value值不存在時(shí),提供一個(gè)默認(rèn)值。但orElse方法是每次均會(huì)計(jì)算默認(rèn)值,無(wú)論option實(shí)例中value值是否存在;而orElseGet方法則是延遲計(jì)算,即只有在option實(shí)例中value值不存在時(shí),才會(huì)去計(jì)算默認(rèn)值。故如果對(duì)于默認(rèn)值的計(jì)算、獲取過(guò)程比較昂貴,推薦使用orElseGet方法

        @Test
        public void test1() {
            Optional<String> optional1 = Optional.ofNullable( "Aaron" );
            Optional<String> optional2 = Optional.ofNullable( null );

            System.out.println("\n-------------------- Test 1: Start --------------------");
            String s1 =  optional1.orElse( getDefault() );
            System.out.println("s1: " + s1);
            System.out.println("-------------------- Test 1: End ----------------------");

            System.out.println("\n-------------------- Test 2: Start --------------------");
            String s2 =  optional2.orElse( getDefault() );
            System.out.println("s2: " + s2);
            System.out.println("-------------------- Test 2: End ----------------------");

            System.out.println("\n-------------------- Test 3: Start --------------------");
            s1 =  optional1.orElseGet( () -> getDefault() );
            System.out.println("s1: " + s1);
            System.out.println("-------------------- Test 3: End ----------------------");

            System.out.println("\n-------------------- Test 4: Start --------------------");
            s2 =  optional2.orElseGet( () -> getDefault() );
            System.out.println("s2: " + s2);
            System.out.println("-------------------- Test 4: End ----------------------");
            
        }

        public String getDefault() {
            System.out.println("call getDefault method");
            return "Tony";
        }

        測(cè)試結(jié)果如下,符合預(yù)期

        figure 10.jpeg

        map與flatMap

        Optional的初衷就是為了解決NPE而設(shè)計(jì)的。比如在傳統(tǒng)的代碼中,為了防止出現(xiàn)NPE,開發(fā)者需要層層判空。示例代碼如下所示,這樣顯然非常繁瑣

        public class OptionalTest {

            @Test
            public void test2() {
                Person person = getPerson();

                String addr = null;
                if(person!=null) {
                    Person.Company company = person.getCompany();
                    if( company!=null ) {
                        addr = company.getAddr();
                    }
                }

                String tel = null;
                if(person!=null) {
                    Person.Company company = person.getCompany();
                    if( company!=null ) {
                        tel = company.getTel();
                    }
                }

                System.out.println("addr: " + addr);
                System.out.println("tel: " + tel);
            }

            /**
             * 獲取Person實(shí)例
             * @return
             */

            public Person getPerson() {
                Person.Family family = Person.Family.builder()
                    .mother("Amy")
                    .build();

                Person person = Person.builder()
                    .name("Tony")
                    .company( Person.Company.builder()
                        .type("外貿(mào)")
                        .addr("廣東省")
                        .build())
                    .family( Optional.ofNullable(family) )
                    .build();

                return person;
            }

            @Data
            @AllArgsConstructor
            @NoArgsConstructor
            @Builder
            public static class Person {
                private String name;

                private Integer age;

                private Company company;

                private Optional<Family> family;

                @Data
                @AllArgsConstructor
                @NoArgsConstructor
                @Builder
                public static class Company {
                    private String type;

                    private String addr;

                    private String tel;
                }

                @Data
                @AllArgsConstructor
                @NoArgsConstructor
                @Builder
                public static class Family {
                    private String father;

                    private String mother;
                }
            }

        }

        測(cè)試結(jié)果如下,符合預(yù)期

        figure 11.jpeg

        而自從有了Optional后,情況就大不一樣了。我們可以使用map、flatMap實(shí)現(xiàn)層層轉(zhuǎn)化。在整個(gè)鏈?zhǔn)秸{(diào)用過(guò)程中一旦某個(gè)Optional的value為null,則會(huì)返回一個(gè)空Optional,繼續(xù)執(zhí)行。以免出現(xiàn)NPE

        @Test
        public void test3() {
            Person person = getPerson();
            Optional<Person> optionalPerson = Optional.ofNullable(person);

            String addr = optionalPerson.map( Person::getCompany )
                .map( Person.Company::getAddr )
                .orElse(null);

            String tel = optionalPerson.map( Person::getCompany )
                .map( Person.Company::getTel )
                .orElse(null);

            String mother = optionalPerson.flatMap( Person::getFamily )
                .map( Person.Family::getMother )
                .orElse(null);

            String father = optionalPerson.flatMap( Person::getFamily )
                .map( Person.Family::getFather )
                .orElse(null);

            System.out.println("addr: " + addr);
            System.out.println("tel: " + tel);
            System.out.println("mother: " + mother);
            System.out.println("father: " + father);
        }

        測(cè)試結(jié)果如下,符合預(yù)期

        figure 12.jpeg

        Note

        • 對(duì)于Map接口提供的putIfAbsent默認(rèn)方法而言,其與computeIfAbsent方法在功能上雖然類似。但有一些不同的地方。首先,computeIfAbsent方法對(duì)于value的計(jì)算是延遲計(jì)算,即只有在key不存在 或 該key在Map中的value為null 時(shí),其才會(huì)計(jì)算value。而putIfAbsent無(wú)論最終是否需要設(shè)置該鍵值對(duì),都會(huì)去計(jì)算value。所以value的計(jì)算如果是一個(gè)昂貴的過(guò)程,推薦使用延遲計(jì)算特性的computeIfAbsent方法;其次,二者返回值不同。computeIfAbsent方法總是會(huì)返回該key在map中相應(yīng)的value值,而putIfAbsent方法,如果 key不存在 或 該key在Map中的value為null 時(shí),會(huì)返回null。否則返回該key在map中相應(yīng)的value值

        參考文獻(xiàn)

        1. Java實(shí)戰(zhàn)·第2版 拉烏爾-加布里埃爾·烏爾瑪、馬里奧·富斯科、艾倫·米克羅夫特著
        瀏覽 51
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            《色戒》电影免费观看高清 | 免费在线观看黄片网站 | 极品少妇久久久 | 囯产精品久久久久久久久久久久久久 | 自拍小说 | 99久久这里只有精品 | 看国产一级精品美女操逼视频 | 成在人线av无码毛片观看网站 | 男女做爰猛烈啪啪吃奶动床戏麻豆 | 作爱视频免费 |