java8 API (一) (String、正则、IO、集合)

前言

目的

  • java8自14年发布已经过去2年多了,越来越多的应用,开始升级到java8,升级不是目的,目的是升级之后,我们就可以使用java8新增的特性,从而提高开发效率、性能、稳定性等。所以伴随着升级,在团队内分享了java8的新特性,不过光了解新特性,知道lambda是啥,真正实践的时候还是有些迷茫(我自己现在就是这样),所以最近开始慢慢的整理java8新增的api,一方面字节在学习,另一方面通过例子的形式,希望可以帮助大家了解新增的api,以及api如何与lambda&stream等新特性应用在开发中。文章中不包含的内容:

    • java8新特性和语法讲很少,如果对java8还不太了解可以先自己查阅学习。
    • 文中例子都比较简单,代码以lambda和stream的代码为主,java8之前的代码例子基本没有,脑补一下,大家都能想出来。
  • 文中例子,都是自己敲过一遍,并且运行出的结果,如果大家发现问题,或者有更好的例子等都欢迎大家随时反馈给我@有光。

java8新增的API

  • 非官方数据

    • 195个新的文件加入到 JDK8 API
    • 93 新类, 89 新接口 , 13 新enum
    • 2699 新方法, 56 新构造函数
    • 46 个接口标记为函数式接口
    • 213 接口默认方法
    • 68 接口静态方法

基础

String

join()

  • 字符串拼接。

        <span class="hljs-keyword">String</span>.<span class="hljs-built_in">join</span>(<span class="hljs-string">":"</span>, <span class="hljs-string">"alibaba"</span>, <span class="hljs-string">"icbu"</span>, <span class="hljs-string">"youguang"</span>);
        <span class="hljs-keyword">String</span> <span class="hljs-built_in">join</span> = <span class="hljs-keyword">String</span>.<span class="hljs-built_in">join</span>(<span class="hljs-string">":"</span>, Lists.newArrayList(<span class="hljs-string">"alibaba"</span>, <span class="hljs-string">"icbu"</span>, <span class="hljs-string">"youguang"</span>));
        System.out.<span class="hljs-built_in">println</span>(<span class="hljs-built_in">join</span>);<span class="hljs-comment">// => alibaba:icbu:youguang</span>
    

chars()

  • 创建一个字符流。
  • 例子:统计去重后的字符数.

        long collect = <span class="hljs-string">"alibaba:icbu:youguang"</span>.chars()//
                                              .<span class="hljs-keyword">distinct</span>()//
                                              .count();
        <span class="hljs-type">System</span>.<span class="hljs-keyword">out</span>.println(collect);// => <span class="hljs-number">11</span>
    

StringJoiner

  • 字符串拼接。
  • PS:String.join方法内部就是用StringJoiner实现的。

         StringJoiner sj = <span class="hljs-keyword">new</span> StringJoiner(<span class="hljs-string">":"</span>);
        sj.<span class="hljs-built_in">add</span>(<span class="hljs-string">"alibaba"</span>);
        sj.<span class="hljs-built_in">add</span>(<span class="hljs-string">"icbu"</span>);
        sj.<span class="hljs-built_in">add</span>(<span class="hljs-string">"youguang"</span>);
        <span class="hljs-keyword">String</span> result = sj.toString(); <span class="hljs-comment">//alibaba:icbu:youguang</span>
    
        sj = <span class="hljs-keyword">new</span> StringJoiner(<span class="hljs-string">":"</span>,<span class="hljs-string">"prefix-"</span>, <span class="hljs-string">"-suffix"</span>);
        sj.<span class="hljs-built_in">add</span>(<span class="hljs-string">"alibaba"</span>);
        sj.<span class="hljs-built_in">add</span>(<span class="hljs-string">"icbu"</span>);
        sj.<span class="hljs-built_in">add</span>(<span class="hljs-string">"youguang"</span>);
        System.out.<span class="hljs-built_in">println</span>(sj.toString()); <span class="hljs-comment">//prefix-alibaba:icbu:youguang-suffix</span>
    

正则

Pattern.splitAsStream()

  • 正则分割后的结果,通过流来操作。
  • 例子:冒号分割,过滤掉youguang

        <span class="hljs-keyword">String</span> <span class="hljs-built_in">bar</span> = Pattern.<span class="hljs-keyword">compile</span>(<span class="hljs-string">":"</span>)
                            .splitAsStream(<span class="hljs-string">"alibaba:icbu:youguang"</span>)
                            .filter(s -> !s.contains(<span class="hljs-string">"youguang"</span>))
                            .collect(Collectors.joining(<span class="hljs-string">":"</span>));
        <span class="hljs-keyword">System</span>.out.println(<span class="hljs-built_in">bar</span>);<span class="hljs-comment">// => alibaba:icbu</span>
    

Pattern.asPredicate()

  • 通过正则生成Predicate。
  • 例子:结合stream来过滤字符串。

      <span class="hljs-built_in">Pattern</span> pattern = <span class="hljs-built_in">Pattern</span>.compile(<span class="hljs-string">".*@gmail\\.com"</span>);
        Stream.of(<span class="hljs-string">"tony.liw@gmail.com"</span>, <span class="hljs-string">"tony.liw@alibaba-inc.com"</span>)
              .filter(pattern.asPredicate())
              .count();<span class="hljs-comment">// => 1</span>
    

数学操作

输入&输出

  • java8提供了更简单的方式进行文件操作。

Files.list

  • Files类从1.7引入,1.8增加list方法生成stream来操作文件/目录。
  • PS:此处使用了1.7中的try-with-resource语法(Stream实现了AutoCloseable接口)

        <span class="hljs-built_in">try</span> (<span class="hljs-built_in">Stream</span><Path> stream = Files.list(Paths.<span class="hljs-built_in">get</span>(<span class="hljs-string">"/opt"</span>))) {
            <span class="hljs-keyword">String</span> joined = stream.<span class="hljs-built_in">map</span>(<span class="hljs-keyword">String</span>::valueOf)<span class="hljs-comment">//</span>
                    .filter(path -> !path.startsWith(<span class="hljs-string">"."</span>))<span class="hljs-comment">//</span>
                     .sorted().collect(Collectors.joining(<span class="hljs-string">"; "</span>));
            System.out.<span class="hljs-built_in">println</span>(<span class="hljs-string">"List: "</span> + joined);<span class="hljs-comment">// => List: /opt/cisco; /opt/local</span>
    

Files.find

  • 查找文件
  • 例子: 找到目录下的所有JavaScript文件(目录查找深度不超过5)。

        Path start = Paths.<span class="hljs-built_in">get</span>(<span class="hljs-string">"/home/admin/"</span>);
        <span class="hljs-keyword">int</span> maxDepth = <span class="hljs-number">5</span>;<span class="hljs-comment">//目录树层数</span>
        <span class="hljs-built_in">try</span> (<span class="hljs-built_in">Stream</span><Path> stream = Files.<span class="hljs-built_in">find</span>(start, maxDepth, (path, attr) ->
                <span class="hljs-keyword">String</span>.valueOf(path).endsWith(<span class="hljs-string">".js"</span>))) {
    
            <span class="hljs-keyword">String</span> joined = stream
                    .sorted()
                    .<span class="hljs-built_in">map</span>(<span class="hljs-keyword">String</span>::valueOf)
                    .collect(Collectors.joining(<span class="hljs-string">"; "</span>));
            System.out.<span class="hljs-built_in">println</span>(<span class="hljs-string">"Found: "</span> + joined);
        }
    

Files.walk

  • 遍历文件,下面例子实现与上面的find同样的功能,不同的是过滤JavaScript文件通过stream的filter实现。

        Path start = Paths.<span class="hljs-built_in">get</span>(<span class="hljs-string">"/home/admin/"</span>);
        <span class="hljs-keyword">int</span> maxDepth = <span class="hljs-number">5</span>;<span class="hljs-comment">//目录树层数</span>
        <span class="hljs-built_in">try</span> (<span class="hljs-built_in">Stream</span><Path> stream = Files.walk(start, maxDepth)) {
            <span class="hljs-keyword">String</span> joined = stream
                    .<span class="hljs-built_in">map</span>(<span class="hljs-keyword">String</span>::valueOf)
                    .filter(path -> path.endsWith(<span class="hljs-string">".js"</span>))
                    .sorted()
                    .collect(Collectors.joining(<span class="hljs-string">"; "</span>));
            System.out.<span class="hljs-built_in">println</span>(<span class="hljs-string">"walk(): "</span> + joined);
        }
    

Files.readAllLines

  • 读取文件的所有内容

    List</span> lines = Files.readAllLines(Paths.get("res/nashorn.js")); lines.add("print('foobar');"); Files.write(Paths.get("res/nashorn1-modified.js"), lines);

PS:使用该方法的时候要考虑到文件的大小,该方法会将所有内容一次读入内存。

Files.lines

  • 将文件内容转成stream,元素是每行的内容。

       <span class="hljs-keyword">try</span> (Stream<<span class="hljs-keyword">String</span>> stream = Files.lines(Paths.<span class="hljs-built_in">get</span>(<span class="hljs-string">"res/nashorn.js"</span>))) {
            stream
                    .<span class="hljs-built_in">filter</span>(<span class="hljs-built_in">line</span> -> <span class="hljs-built_in">line</span>.contains(<span class="hljs-string">"print"</span>))
                    .<span class="hljs-built_in">map</span>(<span class="hljs-keyword">String</span>::<span class="hljs-built_in">trim</span>)
                    .forEach(System.out::<span class="hljs-built_in">println</span>);
        }
    

Files.newBufferedReader&Files.newBufferedWriter

  • 带buffer的读写操作

    Path path = Paths.get(“res/nashorn1.js”); try (BufferedReader reader = Files.newBufferedReader(path)) { System.out.println(reader.readLine()); }

    Path path = Paths.get(“res/output.js”); try (BufferedWriter writer = Files.newBufferedWriter(path)) { writer.write(“print(‘Hello World’);”); }

NULL(空指针)

  • 如何避免NullPointerException,为了避免NPE,写代码过程!=null检查会出现在各种地方,java8引入了更好的解决方案(Optional+Lambda)。其他语言解决NPE的语法糖

Optional基本

    //不要这样,这与!=null没什么区别
    <span class="hljs-keyword">if</span>(stringOptional.isPresent()){
         <span class="hljs-type">System</span>.<span class="hljs-keyword">out</span>.println(stringOptional.get().length());
    }
    //下面是推荐的常用操作

    optionalValue.ifPresent(s -> <span class="hljs-type">System</span>.<span class="hljs-keyword">out</span>.println(s + <span class="hljs-string">" contains red"</span>));
    //增加到集合汇总
    optionalValue.ifPresent(results::add);
    //增加到集合中,并返回操作结果
    <span class="hljs-type">Optional</span><<span class="hljs-type">Boolean</span>> added = optionalValue.map(results::add);

    //无值的optional
    <span class="hljs-type">Optional</span><<span class="hljs-type">String</span>> optionalString = <span class="hljs-type">Optional</span>.empty();
    //不存在值,返回"<span class="hljs-type">No</span> word"
    <span class="hljs-type">String</span> <span class="hljs-literal">result</span>=optionalValue.orElse(<span class="hljs-string">"No word"</span>);
    //没值,计算一个默认值
    <span class="hljs-literal">result</span> = optionalString.orElseGet(() -> <span class="hljs-type">System</span>.getProperty(<span class="hljs-string">"user.dir"</span>));
    //无值,抛一个异常
    <span class="hljs-keyword">try</span> {
        <span class="hljs-literal">result</span> = optionalString.orElseThrow(<span class="hljs-type">NoSuchElementException</span>::new);
    } catch (<span class="hljs-type">Throwable</span> t) {
    }   

Optional.map

  • 例子:看代码应该秒懂了,就是取foo值,但是为了取这个值,正常的逻辑里面需要增加一串!=null检查。java8中可以通过map函数避免。

    //老的写法 Outer outer = new Outer(); if (outer != null && outer.nested != null && outer.nested.inner != null) { System.out.println(outer.nested.inner.foo); } //新的写法 Optional.of(new Outer()) .map(Outer::getNested) .map(Nested::getInner) .map(Inner::getFoo) .ifPresent(System.out::println);

    //第二种lambda的方式 resolve(() -> obj.getNested().getInner().getFoo()) .ifPresent(System.out::println);

    public static Optional resolve(Supplier resolver) { try { T result = resolver.get(); return Optional.ofNullable(result); } catch (NullPointerException e) { return Optional.empty(); } }

集合

Collection

removeIf

  • 通过传入Predicate删除集合里面符合条件的元素。

        <span class="hljs-type">Collection</span><<span class="hljs-type">String</span>> <span class="hljs-built_in">c</span> = new <span class="hljs-type">HashSet</span><>();
        <span class="hljs-built_in">c</span>.add(<span class="hljs-string">"Content 1"</span>);
        <span class="hljs-built_in">c</span>.add(<span class="hljs-string">"Content 2"</span>);
        <span class="hljs-built_in">c</span>.add(<span class="hljs-string">"Content 3"</span>);
        <span class="hljs-built_in">c</span>.removeIf(s -> s.<span class="hljs-built_in">contains</span>(<span class="hljs-string">"2"</span>));
        <span class="hljs-type">System</span>.out.<span class="hljs-built_in">println</span>(<span class="hljs-built_in">c</span>);<span class="hljs-comment">// => [Content 3, Content 1]</span>
    
  • PS:如果这个接口可以满足过滤的要求,就没必要使用stream了,这个效率更好。
  • 扩展:充分利用Predicate,直接看例子

    //基本操作 list =new ArrayList(Arrays.asList(1,2,3,4,5,6,7,8,9,10,11)); list.removeIf(a->{ return a%3==0;});

    //OR 操作 Predicate«span class=”hljs-built_in”>Integer</span>> predicate2 = a->{ return a % 3 == 0;}; Predicate«span class=”hljs-built_in”>Integer</span>> predicate3 = a->{ return a % 5 == 0;}; list =new ArrayList(Arrays.asList(1,2,3,4,5,6,7,8,9,10,11)); list.removeIf(predicate2.or(predicate3));

    //AND 操作 Predicate«span class=”hljs-built_in”>Integer</span>> predicate2 = a->{ return a % 3 == 0;}; Predicate«span class=”hljs-built_in”>Integer</span>> predicate3 = a->{ return a % 5 == 0;}; list =new ArrayList(Arrays.asList(1,2,3,4,5,6,7,8,9,10,11)); list.removeIf(predicate2.and(predicate3));

stream、parallelStream

  • 大家都知道了,就不说了,记住所有集合类都是通过Collection接口继承来即可。

Iterable

forEach

  • 遍历每个元素,继承了Iterable接口的都可以使用。

    List stringList = Arrays.asList("a", "b"); stringList.forEach(System.out::println);

iterator

forEachRemaining

  • 遍历iterator,并根据指定的action进行处理。开发中可能会出现,需要对集合中的第一个或者前几个元素进行特别的操作,然后继续遍历剩余的元素执行另一个操作(action),这个时候使用forEachRemaining非常合适。例如下面字符串拼接的例子。

        <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">iterator</span>.hasNext()) {
            <span class="hljs-keyword">return</span>;
        }
        <span class="hljs-type">StringBuilder</span> builder = new <span class="hljs-type">StringBuilder</span>();
        builder.append(<span class="hljs-keyword">iterator</span>.next());
        <span class="hljs-keyword">iterator</span>.forEachRemaining( element -> {
            builder.append(<span class="hljs-string">", "</span>).append(element);
        });
    
  • PS:这里只是说明一下用法,字符串拼接,当然用String.join就够啦。

List

replaceAll

  • 传入UnaryOperator,将元素替换为一元操作之后的值

    List«span class=”hljs-built_in”>String</span>> stringList = Arrays.asList(“a”, “b”, “c”); stringList.replaceAll(String::toUpperCase); System.out.println(stringList);//[A, B, C]

sort

  • 通过指定Comparator进行排序,参数为空则根据自然排序。

        <span class="hljs-built_in">List</span><<span class="hljs-built_in">String</span>> stringList = Arrays.asList(<span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"c"</span>);
        stringList.sort(<span class="hljs-built_in">String</span><span class="hljs-type">::compareTo</span>);
    
  • PS: 可能会有人问这个和Collections.sort()的区别。下面是Collections的sort方法代码。内部调用的就是list.sort

    public static <T extends Comparable<? supervoid sort(List list) { list.sort(null); }

Set

  • 接口没什么变化。

Map

foreach

       Map<<span class="hljs-keyword">String</span>, Integer> <span class="hljs-built_in">map</span> = new HashMap<>();
        <span class="hljs-built_in">map</span>.<span class="hljs-built_in">put</span>(<span class="hljs-string">"A"</span>, <span class="hljs-number">10</span>);
        <span class="hljs-built_in">map</span>.<span class="hljs-built_in">put</span>(<span class="hljs-string">"B"</span>, <span class="hljs-number">20</span>);
        <span class="hljs-built_in">map</span>.<span class="hljs-built_in">put</span>(<span class="hljs-string">"C"</span>, <span class="hljs-number">30</span>);
        <span class="hljs-built_in">map</span>.forEach((k, v) -> System.out.<span class="hljs-built_in">println</span>(<span class="hljs-string">"Item : "</span> + k + <span class="hljs-string">" Count : "</span> + v));

getOrDefault

  • 根据key取value,没有返回一个默认值(这个一直是很多人想要的方法)。

    System.out.println(map.getOrDefault(“D”,40));// => 40

  • PS:如果还在使用Apache Commons Collections包中的DefaultedMap类,更换了。

putIfAbsent

  • javadoc中提供了与putIfAbsent的等价方法,
  • 特别说明put方法返回值:对应的key曾经有值返回老的value,否则返回null

    //The default implementation is equivalent to, for this map: V v = map.get(key); if (v == null) v = map.put(key, value); return v;

  • 例子:

    System.out.<span class="hljs-built_in">println</span>(<span class="hljs-built_in">map</span>.putIfAbsent(<span class="hljs-string">"B"</span>,<span class="hljs-number">40</span>));<span class="hljs-comment">// => 20</span>
    System.out.<span class="hljs-built_in">println</span>(<span class="hljs-built_in">map</span>.putIfAbsent(<span class="hljs-string">"D"</span>,<span class="hljs-number">40</span>));<span class="hljs-comment">// => null</span>
    

remove(Object.Object)

  • key和valu都相等的时候才删除,下面是javadoc中原来等价方法实现。

      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">map</span>.containsKey(<span class="hljs-built_in">key</span>) && Objects.equals(<span class="hljs-built_in">map</span>.<span class="hljs-built_in">get</span>(<span class="hljs-built_in">key</span>), value)) {
          <span class="hljs-built_in">map</span>.<span class="hljs-built_in">remove</span>(<span class="hljs-built_in">key</span>);
          <span class="hljs-built_in">return</span> <span class="hljs-literal">true</span>;
      } <span class="hljs-keyword">else</span>
          <span class="hljs-built_in">return</span> <span class="hljs-literal">false</span>;
    
  • 例子:

    System.out.println(map.remove(“D”,40));// => true

replace(K,V)

  • map中包含这个key,替换对应的value。javadoc中原来等价的方法实现

      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">map</span>.containsKey(<span class="hljs-built_in">key</span>)) {
          <span class="hljs-built_in">return</span> <span class="hljs-built_in">map</span>.<span class="hljs-built_in">put</span>(<span class="hljs-built_in">key</span>, value);
      } <span class="hljs-keyword">else</span>
          <span class="hljs-built_in">return</span> null;
    
  • 例子:Map<String, Integer> map = new HashMap<>(); map.put("A", 10); System.out.println(map.replace("A",20));//=> 10
  • PS:注意与putIfAbsent区别

replace(K,V,V)

  • map中包含这个key,并且value相等时,替换对应的value。javadoc中原来等价的方法实现

      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">map</span>.containsKey(<span class="hljs-built_in">key</span>) && Objects.equals(<span class="hljs-built_in">map</span>.<span class="hljs-built_in">get</span>(<span class="hljs-built_in">key</span>), value)) {
          <span class="hljs-built_in">map</span>.<span class="hljs-built_in">put</span>(<span class="hljs-built_in">key</span>, newValue);
          <span class="hljs-built_in">return</span> <span class="hljs-literal">true</span>;
      } <span class="hljs-keyword">else</span>
          <span class="hljs-built_in">return</span> <span class="hljs-literal">false</span>;
    
  • 例子:

        Map<<span class="hljs-keyword">String</span>, Integer> <span class="hljs-built_in">map</span> = new HashMap<>();
        <span class="hljs-built_in">map</span>.<span class="hljs-built_in">put</span>(<span class="hljs-string">"A"</span>, <span class="hljs-number">10</span>);
        <span class="hljs-built_in">map</span>.<span class="hljs-built_in">put</span>(<span class="hljs-string">"B"</span>, <span class="hljs-number">20</span>);
        System.out.<span class="hljs-built_in">println</span>(<span class="hljs-built_in">map</span>.replace(<span class="hljs-string">"B"</span>,<span class="hljs-number">10</span>,<span class="hljs-number">100</span>));<span class="hljs-comment">//=>false</span>
        System.out.<span class="hljs-built_in">println</span>(<span class="hljs-built_in">map</span>.replace(<span class="hljs-string">"B"</span>,<span class="hljs-number">20</span>,<span class="hljs-number">100</span>));<span class="hljs-comment">//=> true</span>
    

compute(K,remappingFunction)

  • 通过remappingFunction函数,根据指定的key和对应的value,通过计算生成新的value。javadoc中原来等价的方法实现

      V oldValue = <span class="hljs-built_in">map</span>.<span class="hljs-built_in">get</span>(<span class="hljs-built_in">key</span>);
      V newValue = remappingFunction.<span class="hljs-built_in">apply</span>(<span class="hljs-built_in">key</span>, oldValue);
      <span class="hljs-keyword">if</span> (oldValue != null ) {
         <span class="hljs-keyword">if</span> (newValue != null)
            <span class="hljs-built_in">map</span>.<span class="hljs-built_in">put</span>(<span class="hljs-built_in">key</span>, newValue);
         <span class="hljs-keyword">else</span>
            <span class="hljs-built_in">map</span>.<span class="hljs-built_in">remove</span>(<span class="hljs-built_in">key</span>);
      } <span class="hljs-keyword">else</span> {
         <span class="hljs-keyword">if</span> (newValue != null)
            <span class="hljs-built_in">map</span>.<span class="hljs-built_in">put</span>(<span class="hljs-built_in">key</span>, newValue);
         <span class="hljs-keyword">else</span>
            <span class="hljs-built_in">return</span> null;
      }
    
  • 例子:统计单词出现次数/统计图书借阅次数/遇到同样的key,创建或者append一个msg信息等。

        <span class="hljs-meta">Map</span><<span class="hljs-keyword">String, </span>Integer> countMap = Maps.newHashMap()<span class="hljs-comment">;</span>
        List<<span class="hljs-keyword">String> </span><span class="hljs-keyword">bookList= </span>Arrays.asList(<span class="hljs-string">"Book-A"</span>, <span class="hljs-string">"Book-B"</span>, <span class="hljs-string">"Book-A"</span>)<span class="hljs-comment">;</span>
        for (<span class="hljs-keyword">String </span><span class="hljs-keyword">book </span>: <span class="hljs-keyword">bookList) </span>{
            countMap.compute(<span class="hljs-keyword">book, </span>(k, v) -> v == null ? <span class="hljs-number">1</span> : v + <span class="hljs-number">1</span>)<span class="hljs-comment">;</span>
        }
        System.out.println(countMap)<span class="hljs-comment">;</span>
    
  • PS:要注意如果remappingFunction计算结果为空,会从map中remove

computeIfAbsent(K,mappingFunction)vs computeIfPresent(K,remappingFunction)

  • 这两个方法比较相近容易混淆,对比一下,下面是javadoc中两个新方法等同老代码。左侧:computeIfAbsent,右侧:computeIfPresent screenshot
  • 区别:

    • computeIfAbsent value根据key计算。computeIfPresent value基于key和oldValue计算(参数名也可以看出点意思mappingFunction/remappingFunction)
    • newValue为空,computeIfPresent会删除map中的元素。
  • 例子:这两个方法很容易想到的一个场景是本地缓存(考虑到多线程场景可以使用ConcurrentHashMap),下面以computeIfAbsent方法为例,同时与java8之前的版本代码做一下对比。

    static Map«span class=”hljs-built_in”>String</span>, Integer> CACHE = new ConcurrentHashMap<>(); public Integer getJAVA8(String key){ //java8会使用thread-safe的方式从cache中存取记录 return CACHE.computeIfAbsent(key, s -> { int rt=0; //xxx 计算 rt= return rt; }); } public Integer getJAVA7(String key){ Integer rt = CACHE.get(key); if (rt == null) { //java8 中ConcurrentHashMap复写了computeIfAbsent方法做了线程安全控制 synchronized (CACHE) { rt = CACHE.get(key); if (rt == null) { //xxxx计算 rt= CACHE.put(key, rt); } } } return rt; }

merge(K,V,remappingFunction)

  • 看名字已经理解80%了,直接看javadoc中与老版本等价的代码好了。

      V oldValue = <span class="hljs-built_in">map</span>.<span class="hljs-built_in">get</span>(<span class="hljs-built_in">key</span>);
      V newValue = (oldValue == null) ? value :
                   remappingFunction.<span class="hljs-built_in">apply</span>(oldValue, value);
      <span class="hljs-keyword">if</span> (newValue == null)
          <span class="hljs-built_in">map</span>.<span class="hljs-built_in">remove</span>(<span class="hljs-built_in">key</span>);
      <span class="hljs-keyword">else</span>
          <span class="hljs-built_in">map</span>.<span class="hljs-built_in">put</span>(<span class="hljs-built_in">key</span>, newValue);
    
  • 是不是一眼看过去和computeIfPresent很像。此处diff一下就清晰了,computeIfPresent是在key对应的value存在时才有效。 screenshot

replaceAll(function)

  • 根据function计算结果,替换map所有entry的value

Comparator

  • Comparator在平时开发中用的比较多,java8针对该接口,增加了16个默认方法和静态方法,下面挑一些跟大家分享。

comparing(keyExtractor)

  • 先看例子,生成根据firstName对Person进行排序的Comparator:

    <span class="hljs-comment">//老写法</span>
    Comparator<Person> comparator=<span class="hljs-keyword">new</span> Comparator<Person>() {
        <span class="hljs-meta">@Override</span>
        <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">compare</span><span class="hljs-params">(<span class="hljs-keyword">final</span> Person o1, <span class="hljs-keyword">final</span> Person o2)</span> </span>{
            <span class="hljs-keyword">return</span> o1.name.compareTo(o2.name);
        }
    };
    <span class="hljs-comment">//lambda 写法</span>
    comparator= (o1, o2) -> o1.name.compareTo(o2.name);
    <span class="hljs-comment">//Comparator.comparing方法,提供提取比较key的function即可</span>
    comparator= Comparator.comparing(Person::getName);
    
  • PS:该方法,要求提取的key实现了 Comparable接口。key没有Comparable接口可以使用重载实现comparing(keyExtractor,keyComparator)
  • 重载方法:

    • comparing(keyExtractor,keyComparator)
    • comparingInt(keyExtractor) 提取的key是Integer类型
    • comparingLong(keyExtractor)
    • comparingDouble(keyExtractor)

reversed()

  • 反转比较规则

        <span class="hljs-built_in">List</span><<span class="hljs-built_in">Integer</span>> integerList = Arrays.asList(<span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>);
        <span class="hljs-comment">//正序</span>
        integerList.sort(<span class="hljs-built_in">Integer</span><span class="hljs-type">::compareTo</span>);
        System.out.println(integerList); <span class="hljs-comment">// => [1, 2, 3, 4, 5]</span>
        <span class="hljs-comment">//倒序</span>
        integerList.sort(Comparator.comparing(<span class="hljs-built_in">Integer</span><span class="hljs-type">::intValue</span>).reversed()); <span class="hljs-comment">// => [5, 4, 3, 2, 1]</span>
    

thenComparing(other)

  • 链式比较,以后不用在使用第三方的ComparisonChain类了。
  • 例子:先按字符串长度,再按字符默认顺序排序(忽略大小写)。

    Comparator«span class=”hljs-keyword”>String> </span>cmp = Comparator.comparingInt(String::length) .thenComparing(String.CASE_INSENSITIVE_ORDER);

  • 重载方法

    • thenComparing(keyExtractor,keyComparator)
    • thenComparing(keyExtractor)
    • thenComparingInt(keyExtractor)
    • thenComparingLong(keyExtractor)
    • thenComparingDouble(keyExtractor)

reverseOrder

  • 静态方法,返回与自然排序相反的Comparator(比较元素要实现Comparator接口)

        List<Integer> integerList2 = Arrays.<span class="hljs-keyword">asList</span>(<span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>);
        integerList.<span class="hljs-keyword">sort</span>(Comparator.reverseOrder());
        System.out.<span class="hljs-keyword">println</span>(integerList);<span class="hljs-comment">// => [5, 4, 3, 2, 1]</span>
    

naturalOrder

  • 与reverseOrder相反,默认的自然排序器。等价//c1.compareTo(c2);

nullsFirst(comparator) & nullsLast(comparator)

  • 针对null值指定策略,排在前面(nullsFirst),还是后面(nullsLast)

        List<Integer> integerList3 = Lists.newArrayList(<span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-keyword">null</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>);
        integerList3.<span class="hljs-keyword">sort</span>(Comparator.nullsFirst(Integer::<span class="hljs-keyword">compareTo</span>));
        System.out.<span class="hljs-keyword">println</span>(integerList3);<span class="hljs-comment">// => [null, 1, 3, 4, 5]</span>
        integerList3.<span class="hljs-keyword">sort</span>(Comparator.nullsLast(Integer::<span class="hljs-keyword">compareTo</span>));
        System.out.<span class="hljs-keyword">println</span>(integerList3);<span class="hljs-comment">// => [1, 3, 4, 5, null]</span>
    
  • 这两个比较器一个很大的作用是可以处理结合中有null值的情况,一般默认的比较器遇到null都会抛出NPE,如果不想NPE可以使用nullsFirst/nullsLast包装一下。上面例子:如果不通过Comparator.nullsFirst(Integer::compareTo)生成新的比较器,直接使用Integer::compareTo就会抛NPE异常。

Arrays

  • 工具类中增加了一些对数组相关的流和并行处理的操作。

Stream

  • 将数组转成流进行操作,这样Stream Api就都可以用了stream api

    //数组内容求和 int[] array = new int[]{1,2,3,4,5}; //求和 Arrays.stream(array).sum() //过滤 int[] ms = Arrays.stream(ns).map(n -> n * 2).filter(n -> n % 4 == 0).toArray();

setAll

  • setAll方法提供一个 int -> T的函数接口做参数,int是数组的索引,T是数组的新值。
  • 例子:通过该接口对数组的索引进行操作,然后将指定数组当前索引位置的值赋值为操作后的值。

        int[]<span class="hljs-built_in"> array </span>=<span class="hljs-built_in"> new </span>int[10];
        Arrays.setAll(array, i -> i * 10);
        System.out.println(Arrays.toString(array));// => 输出 [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]        
    

spliterator

Collections

  • emptySortedSet、emptySortedMap、emptyNavigableSet、emptyNavigableMap、unmodifiableNavigableSet、unmodifiableNavigableMap、synchronizedNavigableSet、synchronizedNavigableMap等。

集合&数组转化

数组转集合

  • 没发现java8有什么新增的方式,如果有同学知道的求推荐。老的方式(Arrays/Guava包):

        List<Integer> integerList = Arrays.asList(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>)<span class="hljs-comment">;</span>
        <span class="hljs-comment">// guava</span>
        integerList = Lists.newArrayList(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>)<span class="hljs-comment">;</span>
    

集合转数组

  • 看例子

        <span class="hljs-comment">//原始方式</span>
        Integer[] integerArray = integerList.toArray(new Integer[integerList.size()])<span class="hljs-comment">;</span>
        <span class="hljs-comment">// guava</span>
        <span class="hljs-keyword">int</span>[] intArray = Ints.toArray(integerList)<span class="hljs-comment">;</span>
        <span class="hljs-comment">// java8-基本类型</span>
        intArray = integerList.stream().mapToInt(Integer::intValue).toArray()<span class="hljs-comment">;</span>
         <span class="hljs-comment">// java8-对象类型</span>
        stringArray = stringList.stream().toArray(String[]::new)<span class="hljs-comment">;</span>
    
  • PS: java8新增了可以通过stream将元素转成数组,但是对一般的集合转数组操作没有比以前更好。不过结合新增的stream操作可以轻松实现List转为B[]的功能。例如:将字符串结合,转成字符串大写

    Character[] characterArray = stringList.stream().map(s -> s.charAt(0)).toArray(Character[]::new);

未完待续

  • java8 API (二) stream整理中
Written on August 9, 2016