Java 集合
简介
- 集合 (collection)是存储对象的容器。例如:6个人一个团队(集合)
- 集合与数组类通,数组是存储具体数据的容器。集合的底层有用到数组
这篇文章是加深对集合的理解,探究各种集合的数据结构,让我们脑海有一个清晰地图像 探究步骤很简单,从添加数据的方法开始,在方法里面找到用什么保存数据的,就知道是啥咯 另外建议大家手动翻源码!
List
List 是基础接口,没有什么数据结构,他只是定义了一些基本的方法。包括Set,Map也是一样的
这是List 源码中的部分方法,都是我们常常会用到的
public interface List<E> extends Collection<E> {
int size();
boolean isEmpty();
boolean contains(Object o);
Object[] toArray();
Iterator<E> iterator();
boolean add(E e);
}
ArrayList
ArrayList 就是 一个 Object[] 数组
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
transient Object[] elementData;
LinkedList
LinkedList 就下面那个 Node 节点,是一个双向的链表结构
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
Vector
Vector 也是一个 Object[] 数组
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
protected Object[] elementData;
Stack
Stack 继承了 Vector,因此 push() 也加了 synchronized 是同步的。
Stack 是(栈)的数据结构,就是一个桶,先进后出的特点。
Map
HaspMap
- HaspMap 是 Map 接口 内部的
Entry<K,V> 接口 - 从Node的结构可以看出,是一个单向链表
- 存储使用数组结构,
Node<K,V>[] table; - 所以,HaspMap 的数据结构是 数组+单向链表
- 另外当链表长度 >=7 的时候变成红黑树结构
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
return null;
}
static final int TREEIFY_THRESHOLD = 8;
transient Node<K,V>[] table;
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
在这里我简单的说一下 HaspMap 添加数据的原理:
首先对key取hash值,作为Node 数组的下标,不存在会直接创建新的Node节点,存在继续判断key,存在则覆盖值,不存在就在Node链表后面增加
HashTable
- HashTable 是一个
Entry<?,?>[] 数组 - 利用synchronized 关键字,实现同步,保证多线程安全
public synchronized V put(K key, V value) {
Entry<?,?> tab[] = table;
addEntry(hash, key, value, index);
return null;
}
private transient Entry<?,?>[] table;
TreeMap
- TreeMap 是红黑树结构,时间复杂度为 Olog(n)
- TreeMap 是有序的,通过Comparator的compare进行排序
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key);
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
return null;
}
private transient Entry<K,V> root;
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK;
Entry(K key, V value, Entry<K,V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
}
ConcurrentHashMap
- ConcurrentHashMap 数据结构和HashMap一致
- 主要通过 Segment<K,V> 分段锁来保证线程安全
- 还采用了 volatile 关键字实现细微的同步
private void writeObject(java.io.ObjectOutputStream s)
@SuppressWarnings("unchecked")
Segment<K,V>[] segments = (Segment<K,V>[])
new Segment<?,?>[DEFAULT_CONCURRENCY_LEVEL];
}
static class Segment<K,V> extends ReentrantLock implements Serializable {
private static final long serialVersionUID = 2249069246763182397L;
final float loadFactor;
Segment(float lf) { this.loadFactor = lf; }
}
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
Node(int hash, K key, V val, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.val = val;
this.next = next;
}
}
transient volatile Node<K,V>[] table;
Set
HashSet
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object();
TreeSet
- TreeSet 就是一个 TreeMap,用来排序数据的
- TreeSet 自定义排序规则,可以使用第二构造器,传入Comparator,重写
compare() 比较规则
public TreeSet() {
this(new TreeMap<E,Object>());
}
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
Collections
- Collections 是集合的工具类,即Collection接口的包装器
- 主要提供一些对集合进行操作的静态方法,了解一些常用的方法
Collections
.emptyList();
.sort(list);
.shuffle(list);
.unmodifiableList(list);
.synchronizedList(list);
.synchronizedSet(set);
.synchronizedMap(map);
关于
1、关于集合的构造函数的初始化参数
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
public Hashtable() {
this(11, 0.75f);
}
- 初始容量,一般都有默认的静态常量,因此也会有对应的扩容方法
- 负载因子:一般为0.75,当集合的容量超过自身容量0.75倍时,进行扩容
2、关于 volatile 关键字的理解
|