<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          淺談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é)果顯然易見(jiàn),如下所示,刪除失敗。原因自然是不言自明。在迭代器遍歷過(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中沒(méi)有對(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中保存各人所喜歡的書(shū)單的方法。每次在向value中添加書(shū)時(shí),都需要判斷該value是否為空,非常繁瑣

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

              // 場(chǎng)景: 給Amy喜歡的書(shū)單添加《老人與?!?/span>
              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è)人喜歡的書(shū)單, key: 人名; value: 書(shū)名列表
              Map<String, List<String>> map = new HashMap<>();

              // 場(chǎng)景: 給Amy喜歡的書(shū)單添加《老人與?!?/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為集合類(lèi)型、且需要向該集合中添加元素的場(chǎng)景。測(cè)試結(jié)果如下,符合預(yù)期

          figure 4.jpeg

          與此同時(shí),computeIfAbsent方法也適用于緩存信息的場(chǎng)景。在下面的示例中,對(duì)于已有簽名數(shù)據(jù)作為Value的Key,顯然沒(méi)有必要重復(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中保存各人所喜歡的書(shū)單的方法。每次從value中移除書(shū)時(shí),都需要判斷該value是否為空,非常繁瑣

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

              // 場(chǎng)景: Amy喜歡的書(shū)單中不能有《金瓶梅》
              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è)人喜歡的書(shū)單, key: 人名; value: 書(shū)名列表
              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天入門(mén)理發(fā)")) );

              // 場(chǎng)景: Amy喜歡的書(shū)單中不能有《金瓶梅》
              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喜歡的書(shū)單中不能有《三年高考五年模擬》
              name = "Bob";
              String bookName2 = "三年高考五年模擬";
              map.computeIfPresent(name, (key, value) -> {
                  value.remove( bookName2 );
                  return value;
              } );

              // 場(chǎng)景: 用戶(hù)Tony注銷(xiāo)了,不需要再保存其喜歡的書(shū)單
              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為集合類(lèi)型、且需要從集合中移除元素的場(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類(lèi)型),其定義了合并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,開(kāi)發(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方法在功能上雖然類(lèi)似。但有一些不同的地方。首先,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)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色成人视频网站免费看 | 人妻夜夜夜夜夜夜 | 国产九九精品视频 | 北条麻妃中文字幕一区 | 黄色一级大片在线免费看国产 |