• JDK1.8 新特性(未完待续)


    本文主要介绍了JDK1.8版本中的一些新特性,乃作者视频观后笔记,仅供参考。

    jdk1.8新特性知识点:

    • Lambda表达式
    • 函数式接口
    • *方法引用和构造器调用
    • Stream API
    • 接口中的默认方法和静态方法
    • 新时间日期API
    在jdk1.8中对hashMap等map集合的数据结构优化。hashMap数据结构的优化 
    原来的hashMap采用的数据结构是哈希表(数组+链表),hashMap默认大小是16,一个0-15索引的数组,如何往里面存储元素,首先调用元素的hashcode 
    方法,计算出哈希码值,经过哈希算法算成数组的索引值,如果对应的索引处没有元素,直接存放,如果有对象在,那么比较它们的equals方法比较内容 
    如果内容一样,后一个value会将前一个value的值覆盖,如果不一样,在1.7的时候,后加的放在前面,形成一个链表,形成了碰撞,在某些情况下如果链表 
    无限下去,那么效率极低,碰撞是避免不了的 
    加载因子:0.75,数组扩容,达到总容量的75%,就进行扩容,但是无法避免碰撞的情况发生 
    在1.8之后,在数组+链表+红黑树来实现hashmap,当碰撞的元素个数大于8时 & 总容量大于64,会有红黑树的引入 
    除了添加之后,效率都比链表高,1.8之后链表新进元素加到末尾 
    ConcurrentHashMap (锁分段机制),concurrentLevel,jdk1.8采用CAS算法(无锁算法,不再使用锁分段),数组+链表中也引入了红黑树的使用

    Lambda表达式

    lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码
    

      先来体验一下lambda最直观的优点:简洁代码

    //匿名内部类
      Comparator<Integer> cpt = new Comparator<Integer>() {
          @Override
          public int compare(Integer o1, Integer o2) {
              return Integer.compare(o1,o2);
          }
      };
    
      TreeSet<Integer> set = new TreeSet<>(cpt);
    
      System.out.println("=========================");
    
      //使用lambda表达式
      Comparator<Integer> cpt2 = (x,y) -> Integer.compare(x,y);
      TreeSet<Integer> set2 = new TreeSet<>(cpt2);
    

      只需要一行代码,极大减少代码量!!

    这样一个场景,在商城浏览商品信息时,经常会有条件的进行筛选浏览,例如要选颜色为红色的、价格小于8000千的

    // 筛选颜色为红色
    public  List<Product> filterProductByColor(List<Product> list){
        List<Product> prods = new ArrayList<>();
        for (Product product : list){
            if ("红色".equals(product.getColor())){
                prods.add(product);
            }
        }
        return prods;
     }
    
    // 筛选价格小于8千的
    public  List<Product> filterProductByPrice(List<Product> list){
        List<Product> prods = new ArrayList<>();
        for (Product product : list){
            if (product.getPrice() < 8000){
                prods.add(product);
            }
        }
        return prods;
     }

    我们发现实际上这些过滤方法的核心就只有if语句中的条件判断,其他均为模版代码,每次变更一下需求,都需要新增一个方法,然后复制黏贴,假设这个过滤方法有几百行,那么这样的做法难免笨拙了一点。如何进行优化呢?

    优化一:使用设计模式

    定义一个MyPredicate接口

    public interface MyPredicate <T> {
        boolean test(T t);
    }

    如果想要筛选颜色为红色的商品,定义一个颜色过滤类

    public class ColorPredicate implements MyPredicate <Product> {
    
         private static final String RED = "红色";
    
         @Override
         public boolean test(Product product) {
             return RED.equals(product.getColor());
         }

    定义过滤方法,将过滤接口当做参数传入,这样这个过滤方法就不用修改,在实际调用的时候将具体的实现类传入即可。

    public List<Product> filterProductByPredicate(List<Product> list,MyPredicate<Product> mp){
            List<Product> prods = new ArrayList<>();
            for (Product prod : list){
                if (mp.test(prod)){
                    prods.add(prod);
                }
            }
            return prods;
        }

    例如,如果想要筛选价格小于8000的商品,那么新建一个价格过滤类既可

    public class PricePredicate implements MyPredicate<Product> {
        @Override
        public boolean test(Product product) {
            return product.getPrice() < 8000;
        }
    }

    这样实现的话可能有人会说,每次变更需求都需要新建一个实现类,感觉还是有点繁琐呀,那么再来优化一下

    优化二:使用匿名内部类

    定义过滤方法:

    public List<Product> filterProductByPredicate(List<Product> list,MyPredicate<Product> mp){
            List<Product> prods = new ArrayList<>();
            for (Product prod : list){
                if (mp.test(prod)){
                    prods.add(prod);
                }
            }
            return prods;
        }

    调用过滤方法的时候:

    // 按价格过滤
    public void test2(){
        filterProductByPredicate(proList, new MyPredicate<Product>() {
            @Override
            public boolean test(Product product) {
                return product.getPrice() < 8000;
            }
        });
    }
    
     // 按颜色过滤
     public void test3(){
         filterProductByPredicate(proList, new MyPredicate<Product>() {
             @Override
             public boolean test(Product product) {
                 return "红色".equals(product.getColor());
             }
         });
     }

    使用匿名内部类,就不需要每次都新建一个实现类,直接在方法内部实现。看到匿名内部类,不禁想起了Lambda表达式。

    优化三:使用lambda表达式

    定义过滤方法:

    public List<Product> filterProductByPredicate(List<Product> list,MyPredicate<Product> mp){
            List<Product> prods = new ArrayList<>();
            for (Product prod : list){
                if (mp.test(prod)){
                    prods.add(prod);
                }
            }
            return prods;
        }

    使用lambda表达式进行过滤

    @Test
    public void test4(){
          List<Product> products = filterProductByPredicate(proList, (p) -> p.getPrice() < 8000);
          for (Product pro : products){
              System.out.println(pro);
          }
      }

    在jdk1.8中还有更加简便的操作 Stream API

    优化四:使用Stream API

    甚至不用定义过滤方法,直接在集合上进行操作

    // 使用jdk1.8中的Stream API进行集合的操作
    @Test
    public void test(){
        // 根据价格过滤
        proList.stream()
               .fliter((p) -> p.getPrice() <8000)
               .limit(2)
               .forEach(System.out::println);
    
        // 根据颜色过滤
        proList.stream()
               .fliter((p) -> "红色".equals(p.getColor()))
               .forEach(System.out::println);
    
        // 遍历输出商品名称
        proList.stream()
               .map(Product::getName)
               .forEach(System.out::println);
    }

    Lmabda表达式的语法总结: () -> ();

     口诀:左右遇一省括号,左侧推断类型省

    注:当一个接口中存在多个抽象方法时,如果使用lambda表达式,并不能智能匹配对应的抽象方法,因此引入了函数式接口的概念

     

    函数式接口

    函数式接口的提出是为了给Lambda表达式的使用提供更好的支持。
    

      什么是函数式接口? 

    简单来说就是只定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口,并且还提供了注解:@FunctionalInterface

    常见的四大函数式接口

    • Consumer 《T》:消费型接口,有参无返回值
        @Test
        public void test(){
            changeStr("hello",(str) -> System.out.println(str));
        }
    
        /**
         *  Consumer<T> 消费型接口
         * @param str
         * @param con
         */
        public void changeStr(String str, Consumer<String> con){
            con.accept(str);
        }
    • Supplier 《T》:供给型接口,无参有返回值
        @Test
        public void test2(){
            String value = getValue(() -> "hello");
            System.out.println(value);
        }
    
        /**
         *  Supplier<T> 供给型接口
         * @param sup
         * @return
         */
        public String getValue(Supplier<String> sup){
            return sup.get();
        }
    • Function 《T,R》::函数式接口,有参有返回值
        @Test
        public void test3(){
            Long result = changeNum(100L, (x) -> x + 200L);
            System.out.println(result);
        }
    
        /**
         *  Function<T,R> 函数式接口
         * @param num
         * @param fun
         * @return
         */
        public Long changeNum(Long num, Function<Long, Long> fun){
            return fun.apply(num);
        }
    • Predicate《T》: 断言型接口,有参有返回值,返回值是boolean类型
    public void test4(){
            boolean result = changeBoolean("hello", (str) -> str.length() > 5);
            System.out.println(result);
        }
    
        /**
         *  Predicate<T> 断言型接口
         * @param str
         * @param pre
         * @return
         */
        public boolean changeBoolean(String str, Predicate<String> pre){
            return pre.test(str);
        }

    在四大核心函数式接口基础上,还提供了诸如BiFunction、BinaryOperation、toIntFunction等扩展的函数式接口,都是在这四种函数式接口上扩展而来的,不做赘述。

    总结:函数式接口的提出是为了让我们更加方便的使用lambda表达式,不需要自己再手动创建一个函数式接口,直接拿来用就好了

    方法引用

    若lambda体中的内容有方法已经实现了,那么可以使用“方法引用” 
    也可以理解为方法引用是lambda表达式的另外一种表现形式并且其语法比lambda表达式更加简单

    (a) 方法引用
    三种表现形式:
    1. 对象::实例方法名
    2. 类::静态方法名
    3. 类::实例方法名 (lambda参数列表中第一个参数是实例方法的调用 者,第二个参数是实例方法的参数时可用)

    public void test() {
            /**
            *注意:
            *   1.lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
            *   2.若lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
            *
            */
            Consumer<Integer> con = (x) -> System.out.println(x);
            con.accept(100);
    
            // 方法引用-对象::实例方法
            Consumer<Integer> con2 = System.out::println;
            con2.accept(200);
    
            // 方法引用-类名::静态方法名
            BiFunction<Integer, Integer, Integer> biFun = (x, y) -> Integer.compare(x, y);
            BiFunction<Integer, Integer, Integer> biFun2 = Integer::compare;
            Integer result = biFun2.apply(100, 200);
    
            // 方法引用-类名::实例方法名
            BiFunction<String, String, Boolean> fun1 = (str1, str2) -> str1.equals(str2);
            BiFunction<String, String, Boolean> fun2 = String::equals;
            Boolean result2 = fun2.apply("hello", "world");
            System.out.println(result2);
        }

    (b)构造器引用 
    格式:ClassName::new

    public void test2() {
    
            // 构造方法引用  类名::new
            Supplier<Employee> sup = () -> new Employee();
            System.out.println(sup.get());
            Supplier<Employee> sup2 = Employee::new;
            System.out.println(sup2.get());
    
            // 构造方法引用 类名::new (带一个参数)
            Function<Integer, Employee> fun = (x) -> new Employee(x);
            Function<Integer, Employee> fun2 = Employee::new;
            System.out.println(fun2.apply(100));
     }

    (c)数组引用

    格式:Type[]::new

    public void test(){
            // 数组引用
            Function<Integer, String[]> fun = (x) -> new String[x];
            Function<Integer, String[]> fun2 = String[]::new;
            String[] strArray = fun2.apply(10);
            Arrays.stream(strArray).forEach(System.out::println);
    }
  • 相关阅读:
    优化eclipse
    Servlet与jsp间的传值问题
    servlet & javabean
    Java数据类型
    CentOS 7 安装tomcat
    Nginx配置详解
    PHP文件缓存实现
    lnmp编译安装
    Php安全规范
    php编码规范
  • 原文地址:https://www.cnblogs.com/shisuiliunian/p/11453770.html
Copyright © 2020-2023  润新知