Java集合

概述Java集合框架的基础接口
Collection 集合的顶级接口,对象的集合(单列集合)
├——-List 接口:元素按进入先后有序保存,元素可重复
│—————-├ LinkedList 接口实现类,底层数据结构为双链表, 插入删除, 没有同步, 线程不安全
│—————-├ ArrayList 接口实现类,可变数组, 随机访问, 没有同步, 线程不安全
│—————-└ Vector 接口实现类 数组, 同步, 线程安全
│ ———————-└ Stack 是Vector类的实现类
└——-Set 接口: 仅接收一次,不可重复,并做内部排序
├—————-└HashSet 接口实现类 底层是hash table数据结构,使用hash表(数组)存储元素,不可重复
│————————└ LinkedHashSet 底层数据结构为哈希表与双链表 元素不可重复
└ —————-TreeSet 底层实现的数据结构为红黑树,源码中直接使用的TreeMap实现操作,特点:元素排好序
└ —————-SortedSet 接口,直接继承自Set
Map 接口 键值对的集合 (双列集合)
├———Hashtable 接口实现类, 同步, 线程安全
├———HashMap 接口实现类 , 底层是哈希表(hash table), 不同步, 线程不安全
│—————–├ LinkedHashMap 双向链表和哈希表实现,不允许重复key
├ ——–TreeMap 底层红黑树对所有的key进行排序,不允许重复key
└———ConcurrentHashMap 线程安全
简述List和Set
首先List 和Set都是接口,继承自Collection接口。
List接口的常见实现类有:ArrayList、LinkedList、Vactor,这三者也都继承了 AbstractList抽象类,LinkedList多继承了一次AbstractSequentialList;
其中,ArrayList实现类的内部实现是动态数组形式,意味着可以根据数组下标随机访问元素,读取效率更高;相对的,写入的效率低,插入和删除的效率相对低;
而LinkedList底层数据结构是双链表(自JDK1.7以后由双向循环链表改为双向链表),由于是链表,所以没有长度限制,添加或删除元素时只需要改变指针指向就可以了(连接新结点或断开某个结点);相对的,访问结点是,链表上需要挨个结点访问,因此LinkedList读取效率相对较低,但插入和删除效率相对较高;(但是实际业务上ArrayList写入效率并不比LinkedList低多少,能用ArrayList尽量用ArrayList;讲个笑话,LinkedList作者自己曾经说过,Josh Bloch:"Does anyone actually use LinkedList?I wrote it,and I never use it。" LinkedList作者自己造轮子,自己不用,好厨子从来不吃自己做的饭)
Vector和ArrayList的功能类似,ArrayList是后面替代Vector的,底层都是使用数组实现的,但是Vector的方法是同步的,线程安全,默认初始容量为10,扩容默认翻倍,可指定扩容的大小;ArrayList如果没有设置容量的话默认初始容量为10,扩容大小为初始容量的50%。
ArrayList 几个重要属性
ArrayList扩容
ArrayList扩容和初始容量是有关的,在jdk1.6、1.7时,ArrayList默认初始容量为10,但是在jdk1.8时,默认初始容量为0(无参构造方法或有参构造指定容量为0时)
我们知道,ArrayList有三种构造方法,一个无参构造,一个含有指定泛型Collection类型集合元素的有参构造(添加Map类型的需要强转)以及一个指定初始容量的有参构造
在无参构造方法中,可以看到:
给elementData 赋值一个常量DEFAULTCAPACITY_EMPTY_ELEMENTDATA(彩蛋:注释 Constructs an empty list with an initial capacity of ten. 1.8的注释中依然是构造一个初始容量为10的集合,这点是错的)
可以看出 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
其实是一个Object类型常量空对象
指定初始容量的有参构造中,给elementData 赋值的也是一个常量空对象:EMPTY_ELEMENTDATA
if判断数组中的元素个数,为零则直接看else if部分:给elementData 赋值一个常量 EMPTY_ELEMENTDATA
添加集合的有参构造中也是如此:
当 传入的初始容量initialCapacity > 0为真时,创建一个大小为initialCapacity的空数组,并将引用赋给elementData;
当 传入的初始容量initialCapacity = 0为真时,将空数组EMPTY_ELEMENTDATA赋给elementData;
当 传入的初始容量initialCapacity < 0为真时,直接抛出IllegalArgumentException异常。
可以看出,无论是有参还是无参构造,虽然给elementData 赋值的是两个常量,但都是空的数组对象,也就是说在初始化的时候,ArrayList默认初始容量是为0的
至于为什么要用两个常量来给elementData 赋值,是因为后面扩容时,可以根据这个标记来判断,该数组是通过无参构造方法初始化的,还是有参构造初始化的,会影响到第一次扩容容量大小;如果是无参构造,在添加第一个元素是,给数组进行第一次扩容,容量为10
第一次添加元素时:
看看ensureCapacityInternal方法:
计算容量:可以从源码上看到,当第一次调用add(E e)方法的时候,判读是不是无参构造函数创建的对象,如果是,将DEFAULT_CAPACITY即10作为ArrayList的容量,此时minCapacity = 1
另外,ArrayList扩容时,如果是无参构造,那么第一次扩容大小为10,第二次扩容大小为0.5倍当前容量,即10+5=15;第三次扩容时,扩容大小是15+7=22;
为什么是+7而不是+7.5?
因为在源码中,以位移运算来计算增加容量的:int newCapacity = oldCapacity + (oldCapacity >> 1);
即 newCapacity 是 当前list容量oldCapacity+ 当前容量右移之后计算的结果,如果当前容量为15,二进制为1111,右移一位后即0111(2)=7(10),所以第三次扩容后,容量大小为15+7=22;
扩容计算方法:
__EOF__

本文链接:https://www.cnblogs.com/destiny-2015/p/17189343.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律