java集合专题List接口ArrayList/Vector/LinkedList底层结构和源码分析

举报
bug郭 发表于 2022/10/06 22:51:13 2022/10/06
【摘要】 大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流作者简介:CSDN java领域新星创作者blog.csdn.net/bug…掘金LV3用户 juejin.cn/user/bug…阿里云社区专家博主,星级博主,developer.a...

大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流

作者简介:

在这里插入图片描述
我们学习List下面ArrayList,LinkedList,Vector类即可!
学习的重点就是了解他们的底层结构,扩容原理,通过源码进行学习!

List接口下的一些常用方法
在这里插入图片描述

@SuppressWarnings("all")
public class ArrayList_ {
    public static void main(String[] args) {
        List list = new ArrayList();
        //添加单个元素
        for (int i = 0; i <10; i++) {
            list.add(i);
        }
        //将list集合转换成数组进行打印!
        System.out.println(Arrays.toString(list.toArray()));
        //添加list集合
        List list1 = new ArrayList();
        list1.add(666);
        list1.add(888);
        //在1索引位置插入list1集合
        list.addAll(1,list1);
        //迭代器打印!
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            System.out.print(iterator.next()+" ");
        }
        //删除元素
        list.remove(new Integer(666));
        //判断是否包含元素!
        list.contains(666);
        //删除整个list
        list.removeAll(list1);
        //返回最后一个9的下标位置!
        System.out.println("\n"+Arrays.toString(list.toArray()));
        System.out.println("从后往前找9的下标:"+list.lastIndexOf(9));
        System.out.println("6的下标:"+list.indexOf(6));
        System.out.println("拆分list集合");
        List tmp = list.subList(5,10);
        System.out.println("tmp:"+Arrays.toString(tmp.toArray()));
        //更改元素!
        System.out.println("更改前:"+list.get(0)+list.set(0,9)+"更改后:"+list.get(0));
        //排序:传入排序接口!
        System.out.println("排序前:"+Arrays.toString(list.toArray())+"\n排序后:");
        list.sort(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return (Integer) o1 - (Integer)o2;//升序
            }
        });
        //增强for遍历!
        for (Object object : list) {
            System.out.print(object+" ");
        }

    }

}

在这里插入图片描述

ArrayList

底层结构

ArrayList底层是由数组实现的!
在这里插入图片描述

  • ArrayList维护了一个elementData数组,由transient修饰不可序列化!

  • ArrayList扩容机制:

    • 无参构造器,默认大小为0,第一次添加元素,扩容为10,后面扩容按照1.5倍进行
    • 有参构造器,默认容量指定参数值大小,不够进行1.5倍扩容

源码分析

我们通过IDEA调试从而分析ArrayList底层结构和扩容机制

无参构造器扩容机制源码分析

我们通过下面的代码进行ArrayList无参扩容机制的分析

public class ArrayList_ {
    public static void main(String[] args) {
        //无参扩容机制!
        ArrayList list = new ArrayList();
        //默认为 0 第一次添加扩容为 10
            for (int i = 0; i <10; i++) {
            list.add(i);
        }
        //后续扩容1.5!
        list.add(11);
    }
}
  • 无参构造器初始化elementData数组长度为0
    在这里插入图片描述
  • 第一次添加
    • 装箱,因为这里的数据是int基本类,要进行装箱成Integer类!

在这里插入图片描述
第一次扩容
在这里插入图片描述
进行扩容后elementData数组长度就变成了10
在这里插入图片描述
当添加第11个元素时,进行二次扩容
在这里插入图片描述

调用int newCapacity = oldCapacity + (oldCapacity>>1);进行1.5倍扩容!
从而数组长度变成了15
在这里插入图片描述

有参构造器扩容机制源码分析

public class ArrayList_1 {
        public static void main(String[] args) {
            //有参扩容机制,初始化指定容量大小
            ArrayList list = new ArrayList(5);
            for (int i = 0; i < 5; i++) {
                list.add(i);
            }
            //进行第一次1.5倍扩容
            list.add(6);
        }
    }
	

在这里插入图片描述
总结:
每次add都会检查数组容量,是否要进行扩容!

  • 无参扩容机制

elementData数组默认为0,当进行第一次add进行第一次扩容,扩容大小为10,而后面进行二次扩容就是1.5倍扩容!

  • 有参扩容机制

elementData数组默认为指定参数长度,第一次扩容时,1.5倍扩容!

Vector

底层结构

Vector类和ArrayList类底层的结构一样也是数组!
不过和ArrayList区别就是,扩容策略不同,Vector是线程安全的(每个方法都加了synchronized),适用于多线程,而ArrayList线程不安全!

扩容策略

  • 无参构造器扩容策略,初始化容量为10,后续2倍扩容
  • 有参构造器,初始化指定参数容量,后续2倍扩容
  • 可设置增量的构造器,指定初始化容量,后续扩容按增量进行

源码分析

在这里插入图片描述
在这里插入图片描述
我们学习Vector构造方法后,发现Vector有个构造器,可以设置容量的增量!
可以自定义扩容的增量值!

扩容机制

通过下面的代码debug进行扩容机制的学习!

public class Vector_ {
    public static void main(String[] args) {
        //Vector无参扩容机制!
        Vector vector = new Vector();
        //一次扩容
        for (int i = 0; i <10 ; i++) {
            vector.add(i);
        }
        //进行二次扩容
        vector.add(10);
    }
}

无参构造器,初始化容量为10
在这里插入图片描述
第一次扩容
在这里插入图片描述
有参扩容机制和无参扩容机制类似,初始化时指定数组长度!扩容策略一样!

LinkedList

底层结构

LinkedList类和上述2个不同,他的底层采用的是双向链表的方式存储数据

  • LinkedList底层实现了双向链表和双端队列的特点
  • 可以添加任意元素包括null
  • 线程不安全,没有实现线程同步
  • 采用链表的方式存储,增删效率高,不需要扩容!

在这里插入图片描述
可以看到LinkedList实现了List接口和DeQue接口!

在这里插入图片描述
Node节点
在这里插入图片描述

这里通过frist保存头Node节点,last保存尾节点!

因为LinkedList实现了Deque双端队列接口,所以其下的方法都实现了
在这里插入图片描述

方法使用

package list;
import java.util.Arrays;
import java.util.LinkedList;
public class LinkedList_ {
    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        linkedList.add(1);
        linkedList.addLast(2);
        linkedList.addFirst(0);
        System.out.println("获取队首元素:"+linkedList.getFirst());
        System.out.println("获取队尾元素:"+linkedList.getLast());
        System.out.println(Arrays.toString(linkedList.toArray()));
        System.out.println("出队:"+linkedList.poll());
        System.out.println(Arrays.toString(linkedList.toArray()));
        System.out.println("对尾删除元素:"+linkedList.pollLast());
        linkedList.push(12);
        linkedList.pop();
        System.out.println(Arrays.toString(linkedList.toArray()));
    }
}

在这里插入图片描述
注意:

这里的方法有点多:offer/poll push/pop add/remove
这里只有push/pop不能进行First/Last组合,其他都可!

源码分析

 public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        list.add(1);
        list.remove();
        list.get(1);
    }

add添加节点

默认队尾入队

在这里插入图片描述
remove删除节点

默认删除第一个节点!

在这里插入图片描述
get方法获取节点

在这里插入图片描述

三者比较

底层结构 增删效率 改查效率
ArrayList/Vector 可变数组 较低,要扩容 较高
LinkedList 双向链表 较高,通过链表修改指针即可 较低

三者如何抉择:

  • 如果改查操作多且单线程使用ArrayList,如果多线程就vector!
  • 如果增删操作多,那就LinkedList
  • 一般来说查询基本上查询为主,所以首选ArrayList!
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。