Java集合类主要由两个根接口Collection和Map派生出来的,Collection派生出了三个子接口:List、 Set、Queue(Java5新增的队列),因此Java集合大致也可分成List、Set、Queue、Map四种接口体系。
注意:
- Collection 是一个集合接口,它提供了对集合对象进行基本 *** 作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。
- Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法:Collections. sort(list)。
- Map不是Collection的子接口。
Java集合框架图如下:
Collection:
- List
- ArrayList
- LinkedList
- Vector
- Stack
- Set
- HashSet
- LinkedHashSet
- TreeSet
Map:
- HashMap
- LinkedHashMap
- TreeMap
- ConcurrentHashMap
- Hashtable
2.线程安全的集合有哪些?线程不安全的呢?
线程安全的:
- Hashtable:比HashMap多了个线程安全。
- ConcurrentHashMap:是一种高效但是线程安全的集合。
- Vector:比Arraylist多了个同步化机制。
- Stack:栈,也是线程安全的,继承于Vector。
线性不安全的:
- HashMap
- Arraylist
- LinkedList
- HashSet
- TreeSet
- TreeMap
- 是否保证线程安全:
- ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
- 底层数据结构:
- Arraylist 底层使用的是Object数组;
- LinkedList 底层使用的是双向循环链表数据结构;
- 插入和删除是否受元素位置的影响:
- ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。
- LinkedList 采用链表 存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近 似 O(n)。
- 是否支持快速随机访问:
- LinkedList 不支持高效的随机元素访问,
- 而ArrayList支持随机访问功能。
- 内存空间占用:
- ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间
- LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
线程安全:
- Vector是线程安全的,Vector在关键性的方法前面都加了 synchronized关键字,来保证线程的安全性,如果有多个线程会访问到集合,那最好是使用 Vector,因为不需要我们自己再去考虑和编写线程安全的代码。
- ArrayList不是线程安全的。
扩容机制:
- ArrayList在底层数组不够用时在原来的基础上扩展0.5倍
- Vector是扩展1倍,这样ArrayList就有利于节约内存空间。
- ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去。 默认情况下,新的容量会是原容量的1.5倍。
- Vector是扩展2倍
没有扩容机制,因为其底层是双向链表结构。不存在数组的扩容一说,没有初始化大小,也没有扩容的机制
5.3 HashSet和HashMap扩容机制HashSet和HashMap都是默认初始容量是16(jdk1.7的),但是jdk1.8做了优化,初始容量为0,第一次存元素的时候才扩容为16,加载因子是0.75,扩容为原来的2倍。
而带LinkedHashSet和LinkedHashMap是链表不存在扩容的
StringBuilder和StringBuffer的初始容量都是16,默认数组容量扩充为原数组容量的2倍+2。
6. Array 和 ArrayList 有什么区别?什么时候该应 Array 而不是 ArrayList 呢?- Array 可以包含基本类型和对象类型,ArrayList 只能包含对象类型。 A
- rray 大小是固定的,ArrayList 的大小是动态变化的。
- ArrayList 提供了更多的方法和特性,比如:addAll(),removeAll(),iterator() 等等。
在JDK1.7 和JDK1.8 中有所差别:
- 在JDK1.7 中,由“数组+链表”组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在 的。
- 在JDK1.8 中,由“数组+链表+红黑树”组成。当链表过长,则会严重影响 HashMap 的性能,红黑树搜索 时间复杂度是 O(logn),而链表是糟糕的 O(n)。因此,JDK1.8 对数据结构做了进一步的优化,引入了红 黑树,链表和红黑树在达到一定条件会进行转换:
- 当链表超过 8 且数据总量超过 64 才会转红黑树。
- 将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不 是转换为红黑树,以减少搜索时间。
解决Hash冲突方法有:
- 开放定址法
- 再哈希法
- 链地址法(拉链法)
- 建立公共溢出区
HashMap中采 用的是 链地址法 。
9. 为什么在解决 hash 冲突的时候,不直接用红黑树?而选择先用链表,再转红黑树?因为红黑树需要进行左旋,右旋,变色这些 *** 作来保持平衡,而单链表不需要。当元素小于 8 个的时 候,此时做查询 *** 作,链表结构已经能保证查询性能。当元素大于 8 个的时候, 红黑树搜索时间复杂度 是 O(logn),而链表是 O(n),此时需要红黑树来加快查询速度,但是新增节点的效率变慢了。 因此,如果一开始就用红黑树结构,元素太少,新增效率又比较慢,无疑这是浪费性能的。
10.HashMap 的put方法流程?以JDK1.8为例,简要流程如下:
- 首先根据 key 的值计算 hash 值,找到该元素在数组中存储的下标;
- 如果数组是空的,则调用 resize 进行初始化;
- 如果没有哈希冲突直接放在对应的数组下标里;
- 如果冲突了,且 key 已经存在,就覆盖掉 value;
- 如果冲突后,发现该节点是红黑树,就将这个节点挂在树上;
- 如果冲突后是链表,判断该链表是否大于 8 ,如果大于 8 并且数组容量小于 64,就进行扩容;如 果链表节点大于 8 并且数组的容量大于 64,则将这个结构转换为红黑树;否则,链表插入键值 对,若 key 存在,就覆盖掉 value。
11.HashSet 和 HashMap 区别?
HashMap | HashSet |
实现了Map接口 | 实现了Set接口 |
存储键值对 | 存储对象 |
HashMap使用键(Key)计算Hashcode | HashSet使用成员对象来计算hashcode, 对于两个对象来说hashcode可能相同, 所以equals()方法来判断对象的相等性, 如果两个对象不同的话,那么返回false |
HashMap相较于HashSet较快,因为他使用唯一的键获取对象 | 较慢 |
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)