数组:数组长度在初始化的时候就已经固定,不适合对象数量未知的情况。
集合(collection、map):长度可变,功能更多。
下图为collection(图片来源于http://blog.csdn.net/Liveor_Die的博客)
下图为map(图片来源于网络)
1.介绍一下list比较常用的集合:(有序,值允许重复)
1)ArrayList:
底层实现:
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // non-private to simplify nested class access
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
初始化方法初始化了一个数组,底层实现为数组。既然是数组,了解下如何实现数组长度的自动化增长。
int newCapacity = oldCapacity + (oldCapacity >> 1);
最关键的一句代码,差不多就是一次增加原有长度的一半,当然还有其他特殊情况的处理,就不详细说了。
特点:末尾添加/删除数据时速度较快,向中间set/remove数据较慢,随机查询效率很高(因为有索引)。
2)LinkedList:
底层实现:
private static class Node {
E item;
Node next;
Node prev;
Node(Node prev, E element, Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
从上图可以看出来是链表,因为有next和prev所以是双向链表。
下面是remove方法的实现代码
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
用遍历来实现,效率肯定低,数据越多效率越低。
特点:add/set/remove方法较快(只需要改变指针地址),随机访问较慢(需要遍历整个链表)。
3)Vector
底层实现:和ArrayList一样是数组,不过Vector所有方法上都有synchronized保证其线程安全。
特点:线程安全,效率低。
2.简单介绍下set:(HashSet无序,不允许有重复值)
HashSet:基于HashMap实现,只存储值,没有key(map中key的位置放置值,value位置放置一个new Object())
HashSet与HashMap的区别:HashMap中keys的元素有校验重复,所以HashSet中存储的对象都会先校验下hashcode。
LinkedHashSet:基于LinkedHashMap实现,保证了元素的有序。
TreeSet:基于TreeMap实现,保证有序。
3.简单介绍下queue:
Deque:双向队列,deque是个接口,实现类有ArrayDeque(底层实现为数组),LinkedDeque(底层实现为链表)。总体来说,arrayDeque要比LinkedDeque性能高。
PriorityQueue:优先级队列,底层实现也是数组,优先级排序,需要实现Comparator(排序方法暂未了解)。
4.另一个基本接口Map
1)HashMap:底层实现为哈希表,所以存储数组不能保证有序。源码如下:
transient Node[] table;
在调用put方法时,会判断table是否为null,如果为null则进行初始化。下边是默认初始化大小:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
当每次数组存满时,当前数组容量小于最大数量(2^30)并且大于初始化容量时,数组容量*2,增加的量如下:
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
HashMap的keys不允许有重复,可以为null,利用hashcode校验keys是否重复,代码如下;
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
2)Hashtable
hashtable在hashmap的基础上所有方法都加上了synchronized关键字。并且在读写操作中限制了key不能为null,否则抛出空指针异常。
3)LinkedHashMap
基于双向链表实现,实际上就是在HashMap中的Node对象中增加了before和after对象,分别存储了上一次put和下一次put的值,以此来维护数据的存入顺序。
static class Entry extends HashMap.Node {
Entry before, after;
Entry(int hash, K key, V value, Node next) {
super(hash, key, value, next);
}
}
4)TreeMap
基于红黑树实现的,该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序。不能够记录存入顺序。
红黑树属于平衡二叉树的一种,数据在存入的时候,会根据数据大小进行排序,即左子节点<根节点<右子节点,所以在查找元素的时候,可以直接和根节点进行比较,如果小于根节点,则向左查找,如果大于根节点,则向右查找,所以查找效率比较高。下边是红黑树的一个图:
(图片来源于:https://blog.csdn.net/q3244676719/article/details/81540830)
关于红黑树的右旋和左旋,请移步至:https://blog.csdn.net/q3244676719/article/details/81540830
(本文部分内容参考上链接总结而出)