草庐IT

【java8新特性】02:常见的函数式接口

请妮妮吃火锅 2023-04-16 原文

Jdk8提供的函数式接口都在java.util.function包下,Jdk8的函数式类型的接口都有@FunctionInterface注解所标注,但实际上即使没有该注解标注的有且只有一个抽象方法的接口,都可以算是函数式接口。
在JDK8中内置的四大核心函数式接口如下:

函数式接口接口类型参数类型返回类型作用Stream流中的应用场景
Consumer<T> 消费型接口 T void 对类型为T的对象进行操作,包含方法为accpet(T t) 如forEach、peek等方法的函数式接口都是Consumer类型
Supplier<T> 供给型接口 T 返回类型为T的对象,包含方法为T get() 如collect等方法的某些方法重载就是用的Supplier类型
Function<T,R> 函数型接口 T R 对类型为T的对象进行操作,返回结果为 R类型的对象,包含方法为R apply(T t) 如map,flatMap等方法的函数式接口都是Function类型
Predicate<T> 断言型接口 T boolean 确定类型为T的对象是否满足约束,并返回约束结果,包含方法为boolean test(T t) 如filter等方法的函数式接口都是Predicate类型

Consumer<T>

Consumer<T>消费型接口,顾名思义就是消费并处理参数,且不反馈调用环境

基本使用

public class Main {
    /**
     * Consumer<T>
     *     消费型接口:顾名思义主要用于消费参数,不反馈调用环境(没有返回值)
     *     accept: 抽象方法实现,用于调用方法。
     *     andThen: 默认实现方法,内部允许我们链式调用
     */
    public static void main(String[] args) {
        // 给定字符串转为大写并输出到控制台,匿名内部类的方式实现
        Consumer<String> con1 = new Consumer<String>() {
            @Override
            public void accept(String str) {
                System.out.println("通过匿名内部类的方式:"+ str.toUpperCase());
            }
        };
        // 执行该方法的时候,我们传入了给定参数字符串,它会去执行我们上述实现的accept方法并传入参数,最后执行我们给定的代码逻辑
        con1.accept("abc");
        // 给定字符串转为大写并输出到控制台,通过Lambda表达式实现
        Consumer<String> con2 = (text)-> System.out.println("通过Lambda表达式的方式:"+ text.toUpperCase());
        con2.accept("goods");
        /**
         * 最终结果:
         * 通过匿名内部类的方式:ABC
         * 通过Lambda表达式的方式:GOOD
         * 使用lambda表达式,我们只需要记住参数列表和执行逻辑即可,其他的我们无需关注。
         */
    }}

学习案例

public class Main {

    /**
     * Consumer<T>
     *     消费型接口:顾名思义主要用于消费参数,不反馈调用环境(没有返回值)
     *     accept: 抽象方法实现,用于调用方法。
     *     andThen: 默认实现方法,内部允许我们链式调用
     */
    public static void main(String[] args) {
        // 1.我们需要将集合进行排序后在输出到控制台
        Consumer<List> con1 = list-> {
            System.out.println("排序前的集合:"+ list);
            Collections.sort(list);
            System.out.println("排序后的集合:"+list);
        };
        con1.accept(Arrays.asList(1,5,3,2,9,6,7));
        /**
         * 最终结果:
         * 排序前的集合:[1, 5, 3, 2, 9, 6, 7]
         * 排序后的集合:[1, 2, 3, 5, 6, 7, 9]
         */
        // 上面执行逻辑实现分两步,第一步需要获取到给定集合进行排序,第二个则是输出排序后的集合
        // 如果以上两个步骤分别用两个consumer也可以实现,我们可以定义一个方法接收两个consumer进行操作
        accept(Arrays.asList(1,5,3,2,9,6,7),list->
        {
            System.out.println("andThen链式调用前集合:"+list);
            Collections.sort(list);
        },list-> System.out.println("andThen链式调用后集合:"+ list));
        /**
         * 最终结果:
         * andThen链式调用前集合:[1, 5, 3, 2, 9, 6, 7]
         * andThen链式调用后集合:[1, 2, 3, 5, 6, 7, 9]
         */
        // 如果consumer参数多个的话,我们可以直接在Lambda表达式进行链式调用,不费那劲定义方法了
        Consumer<List> con2 = ((Consumer<List>) list -> {
            System.out.println("lambda表达式的链式调用前集合:" + list);
            Collections.sort(list);
        }).andThen(list -> System.out.println("lambda表达式的链式调用后集合:"+list));
        // 需要注意的是:要使用这种方式,第一个consumer要进行链式调用必须要强行指定为(Consumer)类型,后续的接口才能够调用方法
        con2.accept(Arrays.asList(1,53,31,25,99,62,17));
        /**
         * 最终结果:
         * lambda表达式的链式调用前集合:[1, 53, 31, 25, 99, 62, 17]
         * lambda表达式的链式调用后集合:[1, 17, 25, 31, 53, 62, 99]
         */
    }

    public static void accept(List<Integer> list,Consumer<List> con1,Consumer<List> con2){
        // 链式调用时会优先执行左边的接口实现,依次往右执行 我们的需求是先排序后输出,第一个Consumer是排序,第二个是输出。
        con1.andThen(con2).accept(list);
    }
}

总结

1.函数式接口的本质实际上就是将函数以参数的形式进行传递

2.Consumer是消费型的函数式接口,通常用于数据内部处理,没有返回值

3.除了Consumer之外,还有各种消费型的函数式接口,还有IntConsumer、LongConsumer等、如果需要传递两个参数则可以使用BIFunction、也可以根据自身需求进行自定义。

Supplier<T>

Consumer<T>供给型函数式接口,顾名思义就是供给数据给调用环境,不接收参数传递

基本使用

public class Main{
    /**
     * 供给型函数式接口顾名思义就是顾名思义就是供给数据给调用环境,不接收参数传递
     *  T get() : 返回泛型T类型的参数到调用环境
     */
    public static void main(String[] args) {
        // 返回一个0-100间的随机数
        Supplier<Integer> sup1 = new Supplier<Integer>() {
            @Override
            public Integer get() {
                int res = new Random().nextInt(100);
                System.out.println("通过匿名内部类的方式获取到的随机数:"+ res);
                return res;
            }
        };
        // 执行该方法的时候,它会去执行我们上述实现的get方法。
        sup1.get();
        // 通过lambda表达式的方式进行实现
        Supplier<Integer> sup2 = ()-> {
            int res = new Random().nextInt(100);
            System.out.println("通过lambda表达式的方式获取到的随机数:"+ res);
            return res;};
        sup2.get();
        /**
         * 最终结果:
         * 通过匿名内部类的方式获取到的随机数:28
         * 通过lambda表达式的方式获取到的随机数:62
         * 使用lambda表达式,我们只需要记住参数列表和执行逻辑即可,其他的我们无需关注。
         */
    }
}

 学习案例

public class Main{

    public static Map<String,String> redis = new HashMap();
    
    /**
     * 供给型函数式接口顾名思义就是顾名思义就是供给数据给调用环境,不接收参数传递
     *  T get() : 返回泛型T类型的参数到调用环境
     */
    public static void main(String[] args) {
        // 1.(模拟)查询某个Key在redis中有没有缓存,缓存没有则从数据库取完存入redis再返回,有的话则直接返回
        String val = getCache("title");
        String val2 = getCache("title");
        String val3 = getCache("title");
        /**
         * 最终结果:
          从数据库中获取:我是标题
          从缓存中获取:我是标题
          从缓存中获取:我是标题
         */
        // 可以看到经过第一次后续都是直接从缓存中取出的数据
    }

    public static String getCache(String key){
        String val = redis.get(key);
        if(Objects.isNull(val)){
            // 获取数据库的数据
            val = getDbVal(() -> "我是标题");
            System.out.println("从数据库中获取:"+val);
            redis.put(key,val);
            return val;
        }
        System.out.println("从缓存中获取:"+val);
        return val;
    }

    public static String getDbVal(Supplier<String> supplier){
       return  supplier.get();
    }
}

总结

1.函数式接口的本质实际上就是将函数以参数的形式进行传递

2.Supplier是供给型的函数式接口,通常用于构建某个对象处理后返回调用环境

3.除了Supplier之外,还有各种供给型的函数式接口,还有BooleanSupplier、IntSupplier等。

Function<T,R>

 Function<T,R>函数型的函数式接口,泛型T是参数、泛型R则是返回值、主要应用场景做数据类型转换等。

基本使用

public class Main{

    /**
     *  Function<T,R>函数型的函数式接口,泛型T是参数、泛型R则是返回值、主要应用场景做数据类型转换等。
     *  R apply(T t): 抽象方法实现,用于调用方法并返回泛型R
     *  <V> Function<T, V> andThen: 默认实现方法,内部允许我们链式调用,与其他的andThen原理一致。
     *  <V> Function<V, R> compose: 默认实现方法,内部允许我们链式调用,调用方式与andThen一样,但执行顺序不一样,compose是先执行compose中的函数接口,再执行左边调用的函数接口,依次往左
     *  <T> Function<T, T> identity():返回当前执行的方法,从源码中我们也可以看到它返回的是当前的t
     */
    public static void main(String[] args) {

        // 传入给定字符串,返回转换后的Integer类型
        Function<String,Integer> fun1 = new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                Integer convert = Integer.valueOf(s);
                System.out.println("通过匿名内部类的方式获取到的值:"+ convert +",数据类型是否为Integer?结果:" + (convert instanceof Integer));
                return convert;
            }
        };
        // 执行该方法的时候,它会去执行我们上述实现的apply方法。
        fun1.apply("10086");
        // 通过lambda表达式的方式进行实现
        Function<String,Integer> fun2 = s->{
            Integer convert = Integer.valueOf(s);
            System.out.println("通过lambda表达式的方式获取到的值:"+ convert +",数据类型是否为Integer?结果:" + (convert instanceof Integer));
            return convert;
        };
        fun2.apply("10000");
        /**
         * 通过匿名内部类的方式获取到的值:10086,数据类型是否为Integer?结果:true
         * 通过lambda表达式的方式获取到的值:10000,数据类型是否为Integer?结果:true
         * 使用lambda表达式,我们只需要记住参数列表和执行逻辑即可,其他的我们无需关注。
         */
        // 我们继续对Function的API做一些理解和补充,毕竟这玩意在工作中经常会用上
        // andThen 我们都知道常用于链式调用的,这里必须保证T和V类型是一样的,也就是参数泛型T和返回值泛型V
        Function<String,String> fun3 = x-> {
            System.out.println("我是fun3的方法");
            return x;
        };
        Function<String,String> fun4 = y-> {
            System.out.println("我是fun4的方法");
            return y;
        };
        fun3.andThen(fun4).apply("test");
        /**
         * 最终结果:
         * 我是fun3的方法
         * 我是fun4的方法
         */
        // 我们发现这里是先执行fun3的apply方法再执行fun4的apply方法的。
        // compose 与andThen一样都是链式调用,但结果却大大不同,这里必须保证T和V类型是一样的,也就是参数泛型T和返回值泛型V
        fun3.compose(fun4).apply("test");
        /**
         * 最终结果:
         * 我是fun4的方法
         * 我是fun3的方法
         */
        // 我们发现这里是先执行的fun4的apply方法再执行fun3的apply方法的
        // 由此我们推断出compose和andThen的区别就在于,compose接口方法执行顺序从右到左,而andThen则是从左到右。
        Function<Object, Object> identity = Function.identity();
        // Function.identity() 静态方法这里就不好演示了,这个通常在后面搭配Stream流转Map类型的时候用到,它返回本身
    }
}

学习案例

public class Main{

    /**
     *  Function<T,R>函数型的函数式接口,泛型T是参数、泛型R则是返回值、主要应用场景做数据类型转换等。
     *  R apply(T t): 抽象方法实现,用于调用方法并返回泛型R
     *  <V> Function<T, V> andThen: 默认实现方法,内部允许我们链式调用,与其他的andThen原理一致。
     *  <V> Function<V, R> compose: 默认实现方法,内部允许我们链式调用,调用方式与andThen一样,但执行顺序不一样,compose是先执行compose中的函数接口,再执行左边调用的函数接口,依次往左
     *  <T> Function<T, T> identity():返回当前执行的方法,从源码中我们也可以看到它返回的是当前的t
     */
    public static void main(String[] args) {
        List<Person> persons = Arrays.asList(new Person(1,"张三"),new Person(2,"李四"));
        // 给定一个person对象集、转换成姓名属性集合返回
        Function<List<Person>,List<String>> fun1 = list-> {
            List<String> arr = new ArrayList<>();
            for (int i = 0;  i < list.size(); i++) {
                arr.add(list.get(i).getName());
            }
            return arr;
        };
        List<String> personNames = fun1.apply(persons);
        System.out.println(personNames);
        /**
         * 最终结果:
         * 结果:[张三, 李四]
         */
    }
}

class Person{
    private Integer id;
    private String name;

    public Person(Integer id, String name) {
        this.id = id;
        this.name = name;
    }


    public void setId(Integer id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }
}

总结

1.函数式接口的本质实际上就是将函数以参数的形式进行传递

2.Function是函数型的函数式接口,通常用于构建某个对象处理后返回调用环境

3.除了Function之外,还有各种函数型的函数式接口,还有BIFunction、ToIntFunction等。

Predicate

 Predicate<T> 断言型的函数式接口,泛型T是参数、返回结果类型为布尔类型的函数接口。

基本使用

public class Main{

    /**
     *  Predicate<T>断言型的函数式接口,泛型T是参数、返回结果类型为布尔类型的函数接口。
     *  boolean test(T t): 抽象方法实现,用于返回传入的参数逻辑运算后布尔类型结果
     *  Predicate<T> and: 默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现 类似于逻辑运算中的短路&操作。
     *  Predicate<T> negate: 默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现,用于将当前判定结果取反后返回,类似于逻辑运算中的!操作
     *  Predicate<T> or:默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现 类似于逻辑运算中的||操作。
     *  Predicate<T> isEqual:静态方法,内部允许我们链式调用,在保证参数不是空的情况下它内部实现逻辑实际上调用的是Object的equals,具体equals看子类有没有重写
     */
    public static void main(String[] args) {
        Predicate<String> pre1 = new Predicate<String>() {
            @Override
            public boolean test(String o) {
                boolean bool = o.matches("[0-9]{1,}");
                System.out.println("通过匿名内部类的方式获取到的值:"+ bool);
                return bool;
            }
        };
        // 执行该方法的时候,它会去执行我们上述实现的test方法。
        pre1.test("10086");
        Predicate<String> pre2 = text->{
            boolean bool = text.matches("[0-9]{1,}");
            System.out.println("通过lambda表达式的方式获取到的值:"+ bool);
            return bool;
        };
        pre2.test("10086a");
        /**
         * 最终结果:
         * 通过匿名内部类的方式获取到的值:true
         * 通过lambda表达式的方式获取到的值:false
         * 使用lambda表达式,我们只需要记住参数列表和执行逻辑即可,其他的我们无需关注。
         */
        // 我们继续对Predicate的API做一些理解和补充,毕竟这玩意在工作中经常会用上
        // and 实际上等价于逻辑运算符中的短路&操作
        Predicate<String> fun3 = x->
        {
            System.out.println("先计算fun3");
            return true;
        };
        Predicate<String> fun4 = x->
        {
            System.out.println("先计算fun4");
            return false;
        };
        System.out.println("第一次and结果:"+fun3.and(fun4).test("test"));
        /**
         * 最终结果:
         * 先计算fun3
         * 先计算fun4
         * 本次结果:false
         */
        // 那么为什么我们知道它是短路&的操作 而不是&的操作呢?,我们只需要将第一个函数式接口返回false,看看它还会不会执行第二个函数式接口即可
        Predicate<String> fun5 = x->
        {
            System.out.println("先计算fun5");
            return false;
        };
        Predicate<String> fun6 = x->
        {
            System.out.println("先计算fun6");
            return true;
        };
        System.out.println("第二次and结果:"+fun5.and(fun6).test("test"));
        /**
         * 最终结果:
         * 先计算fun5
         * 第二次and结果:false。
         */
        // 从结果我们其实可以推断出,在第一个结果为true的情况下第二个fun6压根没进,所以是短路&
        // 并且起始在and方法源码中给我们也可以看到 return (t) -> test(t) && other.test(t); 是短路&

        // negate 实际上等价于逻辑运算符中的!操作
        // 我们直接取上面的值做例子,本来结果应该为false,取反后应该为true
        System.out.println("negate结果:"+fun5.and(fun6).negate().test("test"));
        /**
         * 最终结果:
         * negate结果:true
         */

        // or 等价于逻辑运算符中的||操作
        // 我们直接取上面的做例子,第一个为false,第二个为true、||的最终结果应该为true
        System.out.println("or结果:"+fun5.or(fun6).test("test"));
        /**
         * 最终结果:
         * or结果:true
         */

        // isEqual 内部调用的是Object的equals方法,如果子类重写了equals则调起子类的equals方法
        // 如我们常用的String就重写了Object的equals方法,我们以它做例子
        Predicate<String> fun7 = Predicate.isEqual("Hello");
        System.out.println("isEquals第一次结果:"+ fun7.test("Hello"));
        /**
         * 最终结果:
         * isEquals第一次结果:true
         */
        Predicate<String> fun8 = Predicate.isEqual("World");
        System.out.println("isEquals第二次结果:"+ fun8.test("Hello"));
        // 自定义的对象类型也是可以比较的,但需要重写equals和hashCode,这里就不写示例了,可以自己玩玩

        // 以上就是Predicate的相关API的介绍
    }
}

学习案例

public class Main{

    /**
     *  Predicate<T>断言型的函数式接口,泛型T是参数、返回结果类型为布尔类型的函数接口。
     *  boolean test(T t): 抽象方法实现,用于返回传入的参数逻辑运算后布尔类型结果
     *  Predicate<T> and: 默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现 类似于逻辑运算中的短路&操作。
     *  Predicate<T> negate: 默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现,用于将当前判定结果取反后返回,类似于逻辑运算中的!操作
     *  Predicate<T> or:默认实现方法,内部允许我们链式调用,用于同时判定多个Predicate函数接口的实现 类似于逻辑运算中的||操作。
     *  Predicate<T> isEqual:静态方法,内部允许我们链式调用,在保证参数不是空的情况下它内部实现逻辑实际上调用的是Object的equals,具体equals看子类有没有重写
     */
    public static void main(String[] args) {
        // 判断给定字符串是否纯数字并且小于10 可以使用and进行链式调用
        Predicate<String> pre1 = ((Predicate<String>) s -> s.matches("[0-9]{1,}")).and(x->Integer.valueOf(x) <10);
        System.out.println("使用and方式进行链式调用:"+pre1.test("9"));
        // 需要注意的是:要使用这种方式,第一个Predicate要进行链式调用必须要强行再指定为(Predicate)类型,后续的接口才能够调用方法
        // 实际上这种方式用的比较少,因为比较麻烦,所以一般都会直接使用&&进行判定
        Predicate<String> pre2 = s->  s.matches("[0-9]{1,}") && Integer.valueOf(s) <10;
        System.out.println("使用&&方式调用:"+pre2.test("10"));
        /**
         * 最终结果:
         * 使用and方式进行链式调用:true
         * 使用&&方式调用:false
         */
    }
}

以上就是Jdk8提供的基础的四大函数(除了这四个之外,还有许多函数式接口,当然我们也可以自定义函数式接口)的基本使用方式和一些简单案例,具体该怎么做怎么写则需要根据项目实际需求进行,通常函数式接口都会搭配Stream成套使用,目前也有很多框架支持函数式接口的方式、如MyBatis-plus等社区活跃度较高的框架。

 

有关【java8新特性】02:常见的函数式接口的更多相关文章

  1. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  2. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  3. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  4. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  5. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  6. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  7. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  8. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  9. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  10. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

随机推荐