- 集合源码
- ArrayList
- 底层是数组实现,能够自动扩容,是个动态数组
- 初始化时是个空数组,初始容量大小为10,但不是初始化了一个大小为10的数组
- 第一次add的时候,初始化为容量10
- 自动扩容是当前的1.5倍,使用了右移位运算 >> ,即除于2
- 增删效率慢,通过下标获取速度非常快O(1)
- 增加时会涉及到扩容,删除时会设计到数组元素移动,所以速率慢
- 删除时,如果不是删除尾部的元素,数组会往前移动,效率低
- 增加时,会在尾部增加,尾部增加时效率还是很快的,如果在数组中间增加,则需要将元素右移,速率非常低,当数组容量不够时,需要扩容,扩容需要消耗大量时间
- 非线程安全
- Vector
- 底层是数组实现
- 初始化时可以是数组,也可以是开发指定的数组大小
- 扩容时,默认是当前的两倍,也可以由开发指定扩容的大小
- 线程安全,每个方法加了Syncronized关键字
- 2,3,4是跟ArrayList的区别
- LinkedList
- 底层是双向链表实现
- 初始化时什么也没做,懒惰性增加元素
- 增加时,采用尾插法,将尾部的指针指向当前元素
- 不存在扩容问题,只要内存够大,就能无限增加元素
- 增删效率快,不通过下标获取时的时间复杂度是O(n)
- 通过下标获取节点时,采用的折半查找法,时间复杂度是O(logN)
- 1.6之前采用的是双向循环链表,1.7取消循环,单单双向链表
- HashSet
- 底层还是用的散列表
- 初始化容量是16
- 初始化的时候直接new了一个HashMap
- 增加的时候,直接把值放在HashMap的键位置,因此是散列无序的,且是不能重复的原因
- 增加的值放在HashMap的key位置,value存的是一个静态Object对象
- 扩容时,容量是原来的两倍
- HashMap
- 底层采用的是散列表,解决哈希冲突的方法是“拉链法”
- 初始化容量是16,哈希因子是0.75
- 哈希因子 = 容纳元素个数/数组的长度,因此初始化可容纳的元素个数是12
- 变成红黑树的条件:数组长度大于64,且链表的长达大于8,需这要两个条件
- 当红黑树的节点数量小于6的时候,又会退化为链表
- 通过哈希值计算出在数组上的位置,当产生哈希冲突时,增加链表存储元素
- 1.7是通过头插法建立链表
- 1.8是通过尾插法建立链表
- 当key相等的时候,如果值不一样,就会替换掉值
- 数据结构
- 哈希函数
- 取模法 100%30=3
- 取余法 100/30 = 10
- 平方取中法 11的平方=121 = 2
- 数学计数法
- 折叠法
- 解决哈希冲突的方法
- 线性探测法
- 二次线性探测法
- 拉链法(hashmap采用这种解决冲突,即数组+链表)
- 哈希因子 = 数据个数/数组的容量
- 哈希函数
- Map底层由哈希表实现。
- JDK1.8之前:数组+链表。
- JDK1.8以后:数组+链表,数量多时自动升级为数组+红黑树
- 什么时候链表升级为红黑树?
- 链表长达大于8,且数组大于64时升级为红黑树
- 当链表小于6时,又降为链表
- 为什么加入红黑树?
- 红黑树查找速度快,时间复杂度为logn
- 什么时候链表升级为红黑树?
- 相关解答
- 哈希因子
- 默认是0.75
- 数组长度
- 默认是1<<4 = 16
- 最大容量 1<<30
- 何时扩容?
- 当数组的数据大于16 * 0.75 = 12时,需要扩容
- 扩容机制大小为原来的两倍 大小<<1
- 为啥哈希因子要用0.75
- 0.5太浪费空间
- 1.0密度太大
- 折中取0.75
- 解决冲突(拉链法)
- 当计算出的哈希值一样时,如果键key一样,那么值value会被覆盖
- 如果键key不一样,则新增节点
- Jdk1.7头插法
- Jdk1.8尾插法
- 节点问题
- Jdk1.7用Entry[]
- Jdk1.8用Node[]
- 其实就改了个名字,没有本质的区别
- 哈希因子
- LinkedHashmap
- 有序的hashmap,即取出的元素按照插入的顺序取出
- 原理是put的时候,使用双向链表把put的元素串联起来
- LinkedHashSet
- 有序的hashset,
- 原理是底层用LinkdedHashMap的key作为值存入
- TreeSet
- TreeSet的元素有序,是根据一定的规则进行排序,因为平衡二叉树
- TreeSet可以根据指定的比较器进行排序
- TreeSet不允许元素数据重复
- ArrayList
- 集合比较
- ArrayList和LinkedList的区别
- 两者都是非线程安全
- ArrayList底层是数组,LinkedList底层是双向链表
- 速率问题
- ArrayList通过下标查找速率非常高,直接就是O(1),增加和删除因为涉及扩容或者移动,所以速率是比LinkedList慢的,当然ArrayList在数组尾部增加元素还是蛮快的
- LinkedList查找的时候比ArrayList慢,因为要从头到尾扫一遍,增加和删除的时候速度是非常快的,不用移动元素,只需要控制好指针的指向即可,且LinkedList增加元素是采用的尾插法
- ArrayList和Vector的区别
- Vector初始化时,可以指定数组初始化大小,ArrayList不可以
- Vector是线程安全的,用Syncronized修饰方法,而ArrayList是线程不安全的
- Vecotr默认扩容是原来的2倍,ArrayList默认扩容是原来的1.5倍
- Vecotr可以指定扩容的大小,ArrayList不可以指定扩容的大小,比如Vector可以指定扩容大小为20
- HashSet和HashMap的区别
- HashSet存的元素不能为null,而HashMap可以存一个key为null,可以存多个值为null
- HashSet底层也是使用的HashMap,用HashMap的key来存值
- HashSet是实现的set的接口,HashMap实现的是Map接口
- HashSet增加元素是add方法,HashMap增加元素是put方法
- HashSet存的是对象,HashMap存储的是键值对
- HashTable和HashMap的区别
- HashTable是线程安全的,方法都用Syncronized修饰,而HashMap是线程不安全的
- HashTable不能存key和value不能存null,只要有一个存null,就会报空指针异常,而HashMap可以存一个key为null,可以存多个值为null
- HashTable的效率比HashMap慢
- ConcurrenHashMap和HashMap的区别
- ConcurrenHashMap是线程安全的,HashMap是线程不安全的
- ArrayList和LinkedList的区别
- 关于HashCode
- HashCOde方法是属于Objcet父类提供的方法
- 在Java运行期间,在同一个对象上多次调用hashCode方法时,一致地返回相同的整数
- HashCode的存在主要是用于查找的快捷性,如HashTable,HashMap等,HashCOde是用来在散列存储结构中确定对象的存储地址
- 如果equals方法比较相等,则HashCode值也一定相等,但是HashCode相等,不代表equals比较也相等
- 举例:‘a’和97的哈希值相等,因为ACSCCl码,所以哈希值相等,但是他们的equals肯定是不相等的
- 如果重写了equeals方法,必须要重写HashCOde方法
Java集合总结
最新推荐文章于 2024-09-05 11:00:53 发布