目录
常用容器:
Collection、Collections区别
- java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
- Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
List、Set、Map区别
比较 | List | Set | Map |
---|---|---|---|
继承接口 | Collection | Collection | |
实现类 | ArrayList、LinkedList、Vector | HashSet、LinkedHashSet、TreeSet | HashMap、HashTable、TreeMap |
元素 | 可重复 | 不可重复 | 键不可重复 |
顺序 | 有序 | 无序(实际上由hashcode决定)(LinkedHashSet是有序的) | 无序(LinkedHashMap是有序的) |
线程安全 | Vector线程安全 | HashTable线程安全 |
HashTable、HashMap、HashSet
HashMap与HashTable区别
- HashTable是同步的。HashMap是非同步的,效率上比HashTable高。
- HashMap允许空键空值,HashTable不允许
HashTable详解
哈希函数:在记录的关键字和记录的关键地址之间建立的一种对应关系叫哈希函数。
哈希表应用了哈希函数,由记录的关键字确定记录在表中的地址,并将记录放入此地址,这样构成的表叫哈希表。
HashMap详解
结点:
- 每个node结点存储:定位数据索引位置的
hash值
、key
、value
、指向下一个结点的Node<key,value> next
数据结构:
- HashMap的底层是哈希表,HashMap是“链表散列”的数据结构,即数组和链表的结合体。jdk1.8以后,又加入了红黑树的数据结构。其中,数组部分称为“哈希桶”。
- 向HashMap中插入数据时,首先计算数据的 key 的hashcode,得到一个值,将该值右移十六位得到高十六位哈希值,再将其与原来低十六位值进行异或运算,得到一个新值。将新值与(table.length-1)进行与运算,得到待插入值的下标。然后查看HashMap该下标处,如果还没有值则直接插入新值,如果已经有值了就连接形成链表。
- 当链表中元素的个数达到8个,转化为红黑树形式进行存储,此时查询的时间复杂度由O(n)变为O(logn);当红黑树中元素下降为6时,又转化成链表。
重要参数:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 默认数组长度16。
static final int MAXIMUM_CAPACITY = 1 << 30; // 最大数组容量2^30
static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默认负载因子0.75
static final int TREEIFY_THRESHOLD = 8; // 链表转红黑树的阈值
static final int UNTREEIFY_THRESHOLD = 6; // 扩容时红黑树转链表的阈值
哈希冲突
由于不同内容计算出来的哈希值也可能一样,由hashcode计算到插入位置之后,先查看该位置有无数据,如果有数据,需要调用equals方法进行判断。
动态扩容
初始化长度为16,当添加了12(6*0.75)个元素,开始添加第13个元素时,扩容为原来的两倍,其过程是:1.新建一个数组,长度是原来的两倍 2.遍历原来的数组,重新hash到新数组。为什么不直接复制?因为index的计算方式为hashcode(key)&(length-1)
,在新的数组中位置将改变。而且这样数据不会堆在一起。
HashSet详解
- HashSet 底层由 HashMap 实现
- HashSet的值存放于HashMap的key上,此时HashMap的值统一为PRESENT
ArrayList、Vector、LinkedList
ArrayList、Vector区别
- Vector是同步的。ArrayList是非同步的,快于Vector。
- ArrayList更加通用
ArrayList、LinkedList区别
- ArrayList底层是数组,连续一块内存空间;LinkedList是双向链表,不是连续的内存空间
- ArrayList查找快,因为是连续的内存空间,方便寻址,但删除、插入慢,因为要发生数据迁移;LinkedList查找慢,因为需要通过指针一个个寻找,但删除、插入块,因为只要改变前后结点的指针指向即可。
ArrayList
1.增加
添加到末尾:正常不需要特殊处理,除非现有数组空间不够了,需要扩容。数组初始化容量:10,当容量不够时,按照原先数组容量的1.5倍进行扩容(创建新数组,将原先数组复制到新数组)
添加到中间位置:需要做整体移动
2.删除
删除末尾:不需要迁移。
删除其他位置:需要迁移。
3.修改
修改之前先定位,由于数组是一块连续的内存空间,查找会特别快
LinkedList
LinkedList中,有头尾两个引用first、last
1.增加
添加到末尾:创建一个新的结点,新结点设为last
添加到其他位置,就需要调整前后结点的引用指向
2.修改
修改第一个或最后一个结点很快。
修改其他位置,如果是坐标定位,就按照二分查找法
ArrayList和LinkedList哪个占用空间大?
一般情况下,LinkedList的占用空间更大,因为每个节点要维护指向前后地址的两个节点,但也不是绝对,如果刚好数据量超过ArrayList默认的临时值时,ArrayList占用的空间也是不小的,因为扩容的原因会浪费将近原来数组一半的容量,不过,因为ArrayList的数组变量是用transient关键字修饰的,如果集合本身需要做序列化操作的话,ArrayList这部分多余的空间不会被序列化。
数组、ArrayList区别
- 数组是本地的程序设计组件或者数据结构,ArrayList是一个来自Java集合类的类。实际上,ArrayList 的内部是由一个array实现的。
- 数组可以容纳基本类型和对象,而ArrayList只能容纳对象。
- 数组是固定大小的,而ArrayList大小是可变的。
- 数组没有提供ArrayList那么多功能,比如addAll、removeAll 和 iterator 等。
Queue中 poll() 和 remove() 的区别
poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,remove() 失败的时候会抛出异常。
线程安全的集合
- Vector:几乎在每个方法上都加了synchronized同步锁,效率较低,已经不建议使用
- HashTable:几乎在每个方法上都加了synchronized同步锁,效率较低,已经不建议使用
- Stack
- Enumeration:枚举,是迭代器的早期版本