首页 > 其他分享 >集合框架最详解(包括底层原理)

集合框架最详解(包括底层原理)

时间:2024-07-26 22:02:07浏览次数:13  
标签:list System println add 详解 集合 new 底层 out

集合框架(非常重要)

集合体系结构

在这里插入图片描述

Collection

在这里插入图片描述

在这里插入图片描述

补充:

//把集合转换为指定类型的数组,可以使用下面的代码
String[] array1 = c.toArray(new String[c.size()]);
System.out.println(Arrays.toString(array1)); //[java1,java2, java2, java3]

//把一个集合中的元素,添加到另一个集合中
Collection<String> c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("java3");
c2.add("java4");
c1.addAll(c2); //把c2集合中的全部元素,添加到c1集合中去
System.out.println(c1); //[java1, java2, java3, java4]

遍历方式

Collection<String> c=new ArrayList<>();
        c.add("小亮");
        c.add("小红");
        c.add("小粒");
        
        //1、迭代器遍历
        //1、调用集合iterator()方法返回Iterator对象
        Iterator<String> it = c.iterator();
        //循环遍历
        while (it.hasNext()){
            //迭代器对象.next()返回当前位置的元素
            String next = it.next();
            System.out.println(next);
        }
        
        //2、增强for循环
        for (String s : c) {
            System.out.println(s);
        }
        
        //3、Lambda表达式
        c.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        //简化后:
        c.forEach(s ->System.out.println(s));

对于引用类型,集合中存储的是存储的是地址

List集合

List的特点以及特有方法

在这里插入图片描述

在这里插入图片描述

 //1.创建一个ArrayList集合对象(有序、有索引、可以重复)
        List<String> list = new ArrayList<>();
        list.add("悟空");
        list.add("八戒");
        list.add("沙和尚");
        list.add("唐僧");
        System.out.println(list);//[悟空,八戒,沙和尚,唐僧]
        //2.public void add(int index, E element): 在某个索引位置插入元素
        list.add(2,"白骨精");
        System.out.println(list);//[悟空, 八戒, 白骨精, 沙和尚, 唐僧]

        //3.public E remove(int index): 根据索引删除元素, 返回被删除的元素
        System.out.println(list.remove(2));//白骨精
        System.out.println(list);//[悟空, 八戒, 沙和尚, 唐僧]
        //4.public E get(int index): 返回集合中指定位置的元素
        System.out.println(list.get(0));//悟空
        //5.public E set(int index, E e): 修改索引位置处的元素,修改后,会返回原数据
        System.out.println(list.set(2, "白龙马"));//沙和尚
        System.out.println(list);//[悟空, 八戒, 白龙马, 唐僧]

ArrayList与LinkedList的底层原理

ArrayList的底层原理

在这里插入图片描述

在这里插入图片描述

LinkedList的底层原理

底层原理是双链表

单链表:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

LinkedList的应用场景(适合在一端操作数据的情况):

在这里插入图片描述

LinkedList<String> l=new LinkedList<>();
       //入队
        l.addLast("第1号人");
        l.addLast("第2号人");
        l.addLast("第3号人");
        l.addLast("第4号人");
        System.out.println(l);
        //出队
        System.out.println(l.removeFirst());
        System.out.println(l.removeFirst());
        System.out.println(l.removeFirst());
        System.out.println(l);

在这里插入图片描述

在这里插入图片描述

 LinkedList<String> l=new LinkedList<>();
       //压栈
        l.addFirst("第1号人");
        l.addFirst("第2号人");
        l.addFirst("第3号人");
        l.addFirst("第4号人");
        System.out.println(l);
        //出栈
        System.out.println(l.removeFirst());
        System.out.println(l.removeFirst());
        System.out.println(l.removeFirst());
        System.out.println(l);

在这里插入图片描述

注意:以上的代码不唯一,只要符合数据结构的思想即可。

此外,LinkedList提供了有关栈的方法,方法内部的返回值其实就是之前代码写的:

LinkedList<String> l=new LinkedList<>();
       //压栈(push)
        l.push("第1号人");
        l.push("第2号人");
        l.push("第3号人");
        l.push("第4号人");
        System.out.println(l);
        //出栈(pop)
        System.out.println(l.pop());
        System.out.println(l.pop());
        System.out.println(l.pop());
        System.out.println(l);
Set集合

在这里插入图片描述

HashSet(无序、不重复、无索引)底层原理-哈希表

哈希值:

在这里插入图片描述

一般情况下,大概率两个对象的哈希值是不相同的,但因为hashCode()方法返回的是int类型,那么就会有一定的范围,当对象的数量超过int类型表示的范围后,会有一些对象的哈希值出现相同的情况。

JDK8之前底层原理:哈希表=数组+链表

在这里插入图片描述

无序:因为每添加一个对象,具体的位置是由一定的计算而得,所以说是无序的。

不重复:当两个元素经计算得到的位置相同,则会调用equals方法进行判断是否相等,不等则以链表的形式存入,存入的方式JDK8前后也有区别,JDK8之前是新元素替换老元素的位置,老元素则直接挂在新元素下面形成一个链表;JDK8之后,新元素直接挂在老元素的下面形成一个链表。

抛出一个问题:为什么我们在重写equals()方法时,也要重写hashCode()方法?

答:一般情况下我们重写equals()方法的目的就是为了在比较两个对象时比较的是它们的内容是否一致,那么当我们用hashSet集合去存储我们自定义类型的对象时可能会出现内容相同的对象所计算得到的哈希值不同的情况,哈希值不同了,那么它们所存储的位置就会不同,最终也就导致了一个hashSet集合中存着两个相同的内容,这也就与hashSet的不重复性冲突了。那么这时候的解决方法就是对于自定义类我们去重写它的hashCode方法,让该方法根据对象的内容来生成相应的哈希值,这也就保证了内容相同的不同对象的哈希值一定是相同的,那么在集合中所存储的位置也就一定相同,进而会调用我们重写的equals方法来判断,这解决了上述情况。

无索引:既然无序,那么也就不能根据加入的前后顺序进行检索,也就没了索引。

但是说如果哈希表中的链表过长则导致CRUD的性能下降,因此在JDK8之后引入了红黑树的结构来形成哈希表

在这里插入图片描述

了解一下数据结构中的树:

在这里插入图片描述

在这里插入图片描述

左边的树在查询是相当于一个单链表,查询性能低,所以出现了平衡二叉树。

在这里插入图片描述

LinkedHashSet(有序、不重复、无索引)底层原理

在这里插入图片描述

TreeSet(排序、不重复、无索引)底层原理

在这里插入图片描述

特别注意:自定义类型需要定义它的比较规则,两种方式,一种自定义类实现Comparable接口,另一种传入Comparator接口匿名实现类对象。

在这里插入图片描述

方式二示例:

//        Set<student> lhs=new TreeSet<>(new Comparator<student>() {
//            @Override
//            public int compare(student o1, student o2) {
//                return o1.getAge()- o2.getAge();
//            }
//        });
        //Lambda表达式简化后
        Set<student> lhs=new TreeSet<>(( o1,  o2)-> o1.getAge()- o2.getAge());
		//new student(string name,int age,double height)
        lhs.add(new student("lisa",29,180.2));
        lhs.add(new student("ss",18,180.2));
        lhs.add(new student("dd",24,180.2));
        lhs.add(new student("ff",23,180.2));
        System.out.println(lhs);

底层原理是红黑树(平衡二叉树):

 Set<String> lhs=new TreeSet<>();
        lhs.add("6");
        lhs.add("5");
        lhs.add("5");
        lhs.add("3");
        lhs.add("4");
        lhs.add("7");
        lhs.add("9");
        lhs.add("8");
        System.out.println(lhs);//[3,4,5,6,7,8,9]

形成的红黑树:

在这里插入图片描述

Collection(单列)小结:

在这里插入图片描述

注意:List还有一个subList(开始索引,结束索引)方法,包前不包后,求子集合

补充:
集合的并发修改异常

在这里插入图片描述

对于集合的并发修改异常,本质就是漏删的问题。

List<String> list = new ArrayList<>();
        list.add("王麻子");
        list.add("小李子");
        list.add("李爱花");
        list.add("张全蛋");
        list.add("晓李");
        list.add("李玉刚");
        System.out.println(list); // [王麻子, 小李子, 李爱花, 张全蛋, 晓李, 李玉刚]
        


        //需求:找出集合中带"李"字的姓名,并从集合中删除
        //第一种方式for
        for (int i = 0; i < list.size(); i++) {
            String s=list.get(i);
            if (s.contains("李")){
                list.remove(s);//此处删除后,后面的元素会移到前面来,而i的值仍要+1所以可能会出现漏删的情况
                //解决的方式是,让i在下次循环时仍然要指刚才删除的位置,故此时i--,下次循环时正好指的刚删除的位置
                i--;
            }
        }
        System.out.println(list);//[王麻子, 张全蛋]

        //第二种方式:迭代器循环
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String s=iterator.next();
            if (s.contains("李")){
                //list.remove(s);//此时调用集合的remove方法会导致并发修改异常
                iterator.remove();//迭代器自带一个remove方法,该方法可删除当前遍历元素
            }
        }
        System.out.println(list);//[王麻子, 张全蛋]

注意:增强for循环、Lambda表达式遍历集合时产生并发修改异常时无法更改修复。

分析:增强for循环本身就是对for循环的一种简写,无法进行类似于i–的操作,故无法修改;Lambda表达式中既无法做类似于i–的操作,也无法获取迭代器对象,故也无法修改。

Collection的其他操作

可变参数:

在这里插入图片描述

public static void main(String[] args){
        //不传递参数,下面的nums长度则为0, 打印元素是[]
        test();	
        
        //传递3个参数,下面的nums长度为3,打印元素是[10, 20, 30]
        test(10,20,30); 
        
        //传递一个数组,下面数组长度为4,打印元素是[10,20,30,40] 
        int[] arr = new int[]{10,20,30,40}
        test(arr); 
    }
    
    public static void test(int...nums){
        //可变参数在方法内部,本质上是一个数组
        System.out.println(nums.length);
        System.out.println(Arrays.toString(nums));
        System.out.println("----------------");
    }

注意:

最后还有一些错误写法,写代码时注意一下,不要这么写!!!

  • 一个形参列表中,只能有一个可变参数;否则会报错
  • 一个形参列表中如果多个参数,可变参数需要写在最后;否则会报错

在这里插入图片描述

Collections工具类

注意Collections并不是集合,它比Collection多了一个s,一般后缀为s的类很多都是工具类。这里的Collections是用来操作Collection的工具类。它提供了一些好用的静态方法,如下

在这里插入图片描述

在这里插入图片描述

List<String> list=new ArrayList<>();
        Collections.addAll(list,"小张","小赵","小侯","小刘");
        System.out.println(list);
        //Collections.shuffle()方法相当于洗牌,重新打乱顺序
        Collections.shuffle(list);
        System.out.println(list);
        //排序
        Collections.sort(list);
        System.out.println(list);
        //对自定义类对象排序
        List<student> list1=new ArrayList<>();
        Collections.addAll(list1,new student("lisa", 29, 180.2),new student("hh", 29, 167),
        new student("sir", 29, 170.3),new student("lihua", 29, 170.5));
        Collections.sort(list1, new Comparator<student>() {
            @Override
            public int compare(student o1, student o2) {
                return Double.compare(o1.getHeight(), o2.getHeight());
            }
        });
        System.out.println(list1);

Map集合

在这里插入图片描述

Map常用方法

在这里插入图片描述

Map集合的遍历方式

在这里插入图片描述

第一种方式:根据键找值

// 准备一个Map集合。
        Map<String, Double> map = new HashMap<>();
        map.put("蜘蛛精", 162.5);
        map.put("蜘蛛精", 169.8);
        map.put("紫霞", 165.8);
        map.put("至尊宝", 169.5);
        map.put("牛魔王", 183.6);
        System.out.println(map);
        //第一种方式:根据键值获取value值
        //调取map.keySet()方法获取集合的键值
        Set<String> key = map.keySet();
        System.out.println(key);
        for (String s : key) {
            //根据键值获取value值
            Double v = map.get(s);
            System.out.println(s+"===>"+v);
        }

第二种方式:根据“键值对”遍历

核心:调用map集合的entrySet方法返回该集合的键值对类型集合,在循环中调用getKey()和getValue()方法获取每个键值对的键和值

在这里插入图片描述

//第二种方式:把“键值对”看成一个整体进行遍历
        Set<Map.Entry<String, Double>> entries = map.entrySet();
        for (Map.Entry<String, Double> entry : entries) {
            String key = entry.getKey();//获取键值对的键
            Double value = entry.getValue();//获取键值对的值
            System.out.println(key+"---->"+value);
        }

第三种方式:Lambda表达式遍历

在这里插入图片描述

   //第三种方式:Lambda表达式
//        map.forEach(new BiConsumer<String, Double>() {
//            @Override
//            public void accept(String s, Double aDouble) {
//                System.out.println(s+"--->"+aDouble);
//            }
//        });
        map.forEach(( s,  aDouble) ->
                System.out.println(s+"--->"+aDouble)
        );
小案例:

在这里插入图片描述

 List<String> data=new ArrayList<>();
        String[] places={"A","B","C","D"};
        //随机产生80个景区选择存入集合
        Random random=new Random();
        for (int i = 1; i <= 80; i++) {
            int num = random.nextInt(4);//0,1,2,3
            data.add(places[num]);
        }
        System.out.println(data);
        //统计data集合中的各个数量,并放入map集合中
        Map<String,Integer> map=new HashMap<>();
        for (String s : data) {
            if (map.containsKey(s)){
                //更新value值
                map.put(s,map.get(s)+1);
            }else {
                map.put(s,1);
            }
        }
        System.out.println(map);
HashMap的底层原理

同样也是基于哈希表,其实之前所学的set集合内部返回的就是hashmap对象,只不过是只要了键,没要value值。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

LinkedHashMap底层原理

原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap

在这里插入图片描述

首先将键值对封装成一个Entry对象,然后计算每一个的哈希值放入集合,不同于HashMap的是每一个元素之间多了一个双链表机制记录元素顺序,查找时会按照头指针进行查找。

标签:list,System,println,add,详解,集合,new,底层,out
From: https://blog.csdn.net/2301_76727129/article/details/140724938

相关文章

  • ArrayList底层原理
    1.ArrayList的基本结构  ArrayList内部使用一个Object类型的数组elementData来存储所有的元素。数组的长度可以动态调整。2.初始容量和扩容机制初始容量:当使用无参构造创建一个 ArrayList 实例时会在底层创建一个默认长度为0的数组,可以通过添加参数指定一个初始......
  • DC-2靶机详解
    DC-2信息搜集IP探测netdiscover-r192.168.179.0/24得到ip为192.168.179.133。端口探测先看开放了哪些端口。nmap-sT--min-rate10000-p-192.168.179.133有个7744不知道什么玩意儿。nmap两步走(udp不用扫了,没攻击思路了再说),详细端口扫描nmap-sT-sV......
  • 张量的集合操作
    点击查看代码#-*-coding:utf-8-*-#@Author:钱力#@Time:2024/7/2614:24importtorch#合并操作A=torch.arange(0,16).view(2,8)B=10*AC=torch.cat([A,B],dim=1)#将矩阵根据特定维度进行缝合print(C)D=torch.stack([A,B],dim=1)#......
  • C语言:操作符详解
    文章目录一、操作符(一)操作符的类型1.算术操作符2.移位操作符(1)左移操作符:(1)右移操作符:3.位操作符(二进制)(1)不使用第三个变量,将a和b的值交换。(2)统计整形中二进制中含1的数量(3)编写代码将13二进制序列的第5位修改为1,然后再改回04.赋值操作符5.单目操作符6.关系操作符7.逻辑......
  • 一文详解 JuiceFS 读性能:预读、预取、缓存、FUSE 和对象存储
    在高性能计算场景中,往往采用全闪存架构和内核态并行文件系统,以满足性能要求。随着数据规模的增加和分布式系统集群规模的增加,全闪存的高成本和内核客户端的运维复杂性成为主要挑战。JuiceFS,是一款全用户态的云原生分布式文件系统,通过分布式缓存大幅提升I/O吞吐量,并使用成本较......
  • Java虚拟机详解(JVM)
    目录1.概念与历史背景2.结构与组成2.1类文件格式2.2主要内存区域3.功能与特性3.1类加载机制3.2垃圾回收机制3.3执行引擎3.4安全机制4.实现细节5.应用场景总结Java虚拟机(JavaVirtualMachine,简称JVM)是Java程序运行的核心组件,它为Java程序提供了一个......
  • mybatis的二级缓存详解
    MyBatis的二级缓存是一种强大的查询缓存机制,它在默认情况下是关闭的。要启用二级缓存,需要在SQL映射文件中添加 <cache/> 标签。以下是对MyBatis二级缓存的详细解释:1.基本概念一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为Session。当Session冲......
  • ElasticSearch第1讲(4万字详解 Linux下安装、原生调用、API调用超全总结、Painless、IK
    ElasticSearch官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html非官方中文文档:https://learnku.com/docs/elasticsearch73/7.3极简概括:基于ApacheLucene构建开源的分布式搜索引擎。解决问题:MySQLlike中文全文搜索不走索引......
  • 详解视频中的I帧、P帧、B帧、GOP、IDR 和PTS, DTS
    一.视频传输原理视频是利用人眼视觉暂留的原理,通过播放一系列的图片,使人眼产生运动的感觉。单纯传输视频画面,视频量非常大,对现有的网络和存储来说是不可接受的。为了能够使视频便于传输和存储,人们发现视频有大量重复的信息,如果将重复信息在发送端去掉,在接收端恢复出来,这样就......
  • JavaWed过滤器和监听器(知识回顾+详解)
    过滤器 Filter   1.1概念       在浏览器和目标资源之间进行过滤的中间组件。       请求到达目标资源之前进行过滤。       响应到达浏览器之前进行过滤。   1.2定义过滤器的步骤      1.写一个java类,实现Filter接口   ......