不管哪一种数据结构本质上来说都是一种容器, 都需要搞清楚两点
(1)如何储存
(2)储存特点
数组
数组的逻辑结构是线性的, 属于顺序的存储结构, 一旦申请到内存那么内存就固定了
在数组这个存储结构中所有的数据都是紧密分布的, 中间不能有间隔
查询方式:
int[] numbers = new int[length];
for(int i = 0; i < numbers.length; i++){ //数组的下表从0开始
//TODO 对数组的操作
}
-
优点
- 按照索引查询, 查询效率高
-
缺点
- 添加/删除效率低, 删除和添加都要设计移动数组中的元素
集合
-
java集合类库将接口(interface)与实现(implementation)分离.
-
集合有两个基本接口: Collection 和 Map
Collection
在java集合中,最基本的就是Collection集合
有如下方法:
boolean add(Object o); //添加元素
addAll(Collection other); //添加另一个集合中的所有元素, 结果为两个集合的并集
boolean remove(Object o); //从当前集合中删除第一个找到的与Object对象相同的元素
boolean removeIf(Predicate<? super E> filter); //根据过滤器移除集合中的元素
boolean isEmpty(); //判断当前集合是否为空集合
boolean contains(Object o); //判断是否存在该元素
boolean containsAll(Collection c); //判断集合c中的元素是否在当前集合中都存在
int size(); //获取当前集合长度
boolean retainAll(Collectioj coll); //当前集合仅保留与coll集合相同的元素, 保留两个集合的交集
Object[] toArray(); //返回当前集合中所有元素的数组
<T> T[] toArray(T[] a); //根据集合元素返回数组
Iterator<E> iterator(); //返回一个迭代器对象
Stream<E> stream(); //返回一个Stream流
Stream<E> parallelStream(); //返回一个平行Stream流
void clear(); //删除所有元素
在java 5时Collection接口继承了java.lang.Iterable
接口, 因此Collection集合在java 5之后都可使用foreach
模式遍历
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
boolean removeIf(Predicate<? super E> filter);
removeIf(filter -> filter something)
Iterator
因为Collection结构扩展了Iterator接口, 因此, 对于标准类库中的任何集合都可以使用"foreach循环"
public interface Iterable<E>{
Iterator<T> iterator(); //for each循环可以处理任何实现了Iterable的对象, 因为其返回了Iterator迭代器对象
...
}
Iterator接口有四个方法
public interface Iterator<E>{
E next(); //返回迭代的下一个元素, 若没有下一个元素的情况下进行迭代则抛出NoSuchElementException异常
boolean hasNext(); //返回是否还有元素可以迭代
void remove(); //从collection中移除返回的最后一个元素, 若已经调用过remove再次调用或者没有调用next就调用remve就会抛出IllegalStateException异常
default void forEachRemaining(Consumer<? super E> action); //提供一个lambda表达式,将对迭代器的每一个元素一一调用这个lambda表达式
}
遍历的顺序取决于集合类型, 如果是ArrayList, 迭代器将按照索引从0开始, 每迭代一次, 索引值加1. 如果是访问HashSet中的元素, 则会按照一种基本上随机的顺序获得元素.
HashSet使用的是HashMap中的迭代器, HashMap中的迭代器按照key值进行消费, 所以具有一定的随机性.
public void forEachRemaining(Consumer<? super K> action) {
int i, hi, mc;
if (action == null) //若消费动作为空则抛出空指针异常
throw new NullPointerException();
HashMap<K,V> m = map;
/**
* Node中有四个值
* final int hash; 节点的hash值
* final K key; 节点key
* V value; 节点value
* Node<K,V> next; 下个节点的引用
*/
Node<K,V>[] tab = m.table;
if ((hi = fence) < 0) { // fence: one past last index 过去的最后一个索引
mc = expectedModCount = m.modCount; //表被修改次数, 保证线程安全
hi = fence = (tab == null) ? 0 : tab.length;
}
else
mc = expectedModCount;
if (tab != null && tab.length >= hi &&
(i = index) >= 0 && (i < (index = hi) || current != null)) {
Node<K,V> p = current;
current = null;
do {
if (p == null)
p = tab[i++];
else {
action.accept(p.key); //根据指针指向node值进行消费
p = p.next;
}
} while (p != null || i < hi);
if (m.modCount != mc)
throw new ConcurrentModificationException(); //如果遍历的过程中被修改, 抛出ConcurrentModificationException异常
}
}
ListItr extends Itr implements ListIterator<E>
ArrayList中关于ListIterator的实现(列表迭代器)
/**
* An optimized version of AbstractList.ListItr
*/
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super(); //显示调用父类构造器
cursor = index; //游标指向当前索引
}
public boolean hasPrevious() {
return cursor != 0; //游标不为初始值0
}
public int nextIndex() {
return cursor; //返回游标指向的元素索引
}
public int previousIndex() {
return cursor - 1; //返回游标上一个索引
}
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification(); //检查修改次数
int i = cursor - 1; //赋值为游标前一个索引(返回的当前对象)
if (i < 0)
throw new NoSuchElementException(); //元素缺省, 和Iterator中没有元素的情况下调用next()抛出异常一致
//non-private to simplify nested class access(用于简化嵌套类的访问), 代表元数组
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException(); //检查数组是否被修改过
cursor = i; //游标向前移
return (E) elementData[lastRet = i]; //lastRet 最后返回的数据下标
}
public void set(E e) {
if (lastRet < 0) //集合做remove或add操作之后lastRet置为-1, 对于移除的元素没法进行设置, 所以抛出异常
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e); //设置返回位置的元素为e
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification(); //检查集合前后一致性
try {
int i = cursor; //设置当前游标位置
ArrayList.this.add(i, e); //判断容量是否够, 然后调用System.arraycopy(datas, index, data, index +1, size - index)方法,然后赋值和容量增加
cursor = i + 1; //游标+1
lastRet = -1; //新增元素后最后修改元素重置为1
expectedModCount = modCount; //集合修改操作时modCount增加
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
List
list是一个有序集合. 元素会增加到容器中的特定位置.
-
有两种访问List集合元素的方法
- 使用迭代器Iterator访问
- 使用一个整数索引来访问(随机访问 Random Access), get(int index);
-
List接口的实现类
- Vector类
- ArrayList类
- Stack类
- LinkedList类
default void replaceAll(UnaryOperator<E> operator); //对集合中所有元素执行此操作
default void sort(Comparator<? super E> c); //对集合中元素进行排序, 默认调用Arrays.sort
E get(int index); //根据索引位置取得元素
E set(int index, E element); //设置索引位置的元素
void add(int index, E element); //在索引位置添加元素
E remove(int index); //移除索引位置的元素
int indexOf(Object o); //查找到的第一个该元素的位置
int lastIndexOf(Object o); //查找到的最后一个该元素的位置
List<E> subList(int fromIndex, int toIndex); //集合切割
default Spliterator<E> spliterator(); //用于遍历和区分元素对象, 提供了单独和批量遍历元素的功能
ListIterator<E> listIterator(); //返回一个列表迭代器, 用来访问列表中的元素
**replaceAll(UnaryOperator operator); **
实例:
@Test
public void unaryOperatorTest(){
List<String> list1 = Arrays.asList("abc", "def", "ghi");
UnaryOperator<String> operator1 = (v) -> v.substring(0,1);
list1.replaceAll(operator1);
list1.forEach(System.out::print);
System.out.println();
List<Integer> list2 = Arrays.asList(1, 2, 3);
UnaryOperator<Integer> operator2 = (v) -> v * 2;
list2.replaceAll(operator2);
list2.forEach(System.out::print);
System.out.println();
}
输出:
adg
246
List sort(Comparator<? super E> c);
List中默认调用Arrays.sort(T[] a, Comparator<? super T> c)方法进行分类(排序), 排序完之后使用迭代器赋值给源集合
Arrays.sort(T[] a, Comparator<? super T> c);
TimSort.sort(T[] a, int lo, int hi, Comparator<? super T> c, T[] work, int workBase, int workLen);
countRunAndMakeAscending(T[] a, int lo, int hi, Comparator<? super T> c)
查找有序片段长度并返回
binarySort(T[] a, int lo, int hi, int start,Comparator<? super T> c)
使用二分查找进行排序
TimSort int minRunLength(int n)
进行分片操作
TimSort void pushRun(int runBase, int runLen)
将这个排序片段的起始位置和长度入栈
TimSort void mergeAt
对已经排好序的连续片段进行合并
LinkedList
-
LinkList是一个有序集合, 将每个对象存放在单独的链接中, 每个链接存放着下一个链接的引用
-
在java中所有的链表都是双向链接的
-
JDK1.6之后LinkedList实现了Deque接口. 如果要使用堆栈结构的集合, 也可以考虑使用LinkedList而不是Stack
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first; //头结点
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last; //尾节点
public E getFirst(); //取得头结点中的元素
public E getLast(); //取得尾结点中的元素
public E removeFirst(); //移除头结点,头结点后移
public E removeLast(); //移除尾结点, 尾结点前移
public void addFirst(E e); //添加新的头结点
public void addLast(E e); //添加新的尾结点
public boolean add(E e); //添加一个元素, 实际上调用addLast(E e);
Node<E> node(int index); //根据索引位置取得节点
ListIterator<E> listIterator(int index); //返回new ListItr(int index);
...
}
链表中的节点
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;
}
}
for(int i = 0; i < list.size(); i++){
do something with list.get(i); //这一步操作每一次都会遍历整个链表来取得i位置的节点, 属于虚假的随机访问
}
ArrayList
- 在ArrayList上进行的操作都是非同步的也就是非线程安全的
- ArrayList封装了一个动态再分配的数组,初始为一个空数组, 当添加第一个元素时扩容为10, 而之后调用add(或其他)方法使容量不够时就会进行扩容, ArrayList扩容增加原来的50%(Vector增加1倍)
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10; //初始容量, 若超过这个容量就会进行扩容
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access //transient 无法被持久化的变量类型
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
...
}
ArrayList的扩容机制
private void grow(int minCapacity) { //minCapacity 要求的最小容量
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //增加50%容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //若超出ArrayList最大长度则取Integer.MAX_VALUE
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
Set
-
Set接口是Collection的子接口, 它没有提供额外的方法, 所以同样Set也支持foreach和Iterator.
-
和List不同的是Set集合不允许包含相同的元素.
HashSet
- HashSet和LinkedHashSet按hash算法来存储集合中的元素, 具有很好的存取和查找性能.
- 这种结构无法控制出现的次序, 如果不在意元素的顺序那么这将会是一种很好的结构.
- 散列表为每一个对象计算一个整数hash code, 散列码(hash code)是由对象实例字段得出的一个整数, 有不同数据的对象将产生不同的散列码.
- jdk1.8中HashSet含有一个HashMap对象, 对hashSet的操作实际上都是对hashMap的操作
- HashTable用链表数组实现, 要想查找元素在表中的位置, 要计算他的散列码然后将她的散列码和桶的总数取余, 结果为这个元素在桶中的索引
- 若这时桶已经被填充满, 就需要先与桶中所有元素进行比较, 查看对象是否已经存在,
HashMap计算hash的方式
/* ---------------- Static utilities -------------- */
/**
* Computes key.hashCode() and spreads (XORs) higher bits of hash
* to lower. Because the table uses power-of-two masking, sets of
* hashes that vary only in bits above the current mask will
* always collide. (Among known examples are sets of Float keys
* holding consecutive whole numbers in small tables.) So we
* apply a transform that spreads the impact of higher bits
* downward. There is a tradeoff between speed, utility, and
* quality of bit-spreading. Because many common sets of hashes
* are already reasonably distributed (so don't benefit from
* spreading), and because we use trees to handle large sets of
* collisions in bins, we just XOR some shifted bits in the
* cheapest possible way to reduce systematic lossage, as well as
* to incorporate impact of the highest bits that would otherwise
* never be used in index calculations because of table bounds.
*/
static final int hash(Object key) {
int h;
//保证hashCode不变的情况下, 把hashCode和hashCode高16位进行异或运算, 这种计算方法是灵活性和实用性的一种权衡
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
HashMap扩容:
/**
* Initializes or doubles table size. If null, allocates in
* accord with initial capacity target held in field threshold.
* Otherwise, because we are using power-of-two expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
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; // 若原来有值小于允许的最大值, 且大于16(默认桶大小), 则进行扩容扩容为2的幂数倍
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults 第一次装填数据时, 桶初始化
newCap = DEFAULT_INITIAL_CAPACITY; // 默认桶大小为16
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); // 装填因子默认为75%
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) { //自动再散列
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
LinkedHashSet
- LinkedHashSet是HashSet的子类, 他在HashSet的基础上, 在节点中增加两个属性, before和after, 维护了节点前后添加功能
- 相比于HashSet, LinkedHashSet插入性能有所不足, 而迭代访问所有元素中LinkedHashSet有较好的性能
TreeSet
- TreeSet和HashSet十分相似, 但她是一个有序集合. 可以任意顺序将元素插入到集合中
- TreeSet的排序使用红黑数实现, 添加元素时总会将其放到正确的排序位置上, 因此迭代器可以有序的访问每一个元素
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
...
}
例子:
@Test
public void treeTest(){
Apple appleOne = new Apple("red", 1);
Apple appleTwo = new Apple("blue", 2);
Apple appleThree = new Apple("green", 3);
Set<Apple> set = new TreeSet<>(); //使用Apple实现的比较器
set.add(appleOne);
set.add(appleTwo);
set.add(appleThree);
System.out.println(set);
TreeSet<Apple> apples = new TreeSet<>(Comparator.comparing(Apple::getId)); //创建TreeSet时传入比较器
apples.addAll(set);
System.out.println(apples);
}
private class Apple implements Comparable<Apple>{
private int id;
private String color;
public Apple(String _color, int _id){
this.color = _color;
this.id = _id;
}
@Override
public int compareTo(Apple o) {
return color.compareTo(o.getColor());
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
return (((Apple)obj).getColor().equalsIgnoreCase(this.color));
}
//Getter ...
//Setter ...
@Override
public String toString() {
return " [color: "+ color + ", id = " + id + "] ";
}
}
输出:
[ [color: blue, id = 2] , [color: green, id = 3] , [color: red, id = 1] ]
[ [color: red, id = 1] , [color: blue, id = 2] , [color: green, id = 3] ]
Map
- 映射(Map)用来存放键值对(key, value), key和value可以是任何类型的值
- 键值对的映射是唯一的总能通过key找到唯一的value, 所以key不允许重复,而value允许重复
int size(); //取得大小
boolean isEmpty(); //判断为空
boolean containsKey(Object key); //判断是否包含key
boolean containsValue(Object value); //判断是否包含value
V put(K key, V value); //添加(key, value)映射
void putAll(Map<? extends K, ? extends V> m); //添加m中的所有映射
V get(Object key); //取得value
V remove(Object key); //移除(key, xxx)映射
Set<K> keySet(); //返回key集
Collection<V> values(); //返回value集合
Set<Map.Entry<K, V>> entrySet(); //返回键值对组成的EntrySet
default V getOrDefault(Object key, V defaultValue); //若get(key)==null返回defaultValue, 否则返回get(key)
default void forEach(BiConsumer<? super K, ? super V> action); //遍历所有元素并执行操作apply(k,v), 不对原本map产生影响
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function); //对所有元素v=apply(k,v)然后将v返回到map中
default V putIfAbsent(K key, V value); //若get(key) == null,则put(key, value), 不会覆盖以前的key值
default boolean remove(Object key, Object value); //存在(key, value的情况下), remove(key)
default boolean replace(K key, V oldValue, V newValue); //保证存在(key, oldValue)的情况下, put(key, newValue);
default V replace(K key, V value); //若存在get(key), 则put(key, value)
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction); //计算新值并返回, 若不存在则创建
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
// ise thrown from function is not a cme.
v = function.apply(k, v);
try {
entry.setValue(v);
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
}
}
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
Set keySet();
HashMap
- HashMap是线程不安全的, 允许使用null键, null值
//为空判断
public boolean isEmpty() {
return size == 0;
}
//取值
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
//包含判断
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
//移除映射
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ? null : e.value;
}
//添加映射
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/* ---------------- Static utilities -------------- */
/**
* Computes key.hashCode() and spreads (XORs) higher bits of hash
* to lower. Because the table uses power-of-two masking, sets of
* hashes that vary only in bits above the current mask will
* always collide. (Among known examples are sets of Float keys
* holding consecutive whole numbers in small tables.) So we
* apply a transform that spreads the impact of higher bits
* downward. There is a tradeoff between speed, utility, and
* quality of bit-spreading. Because many common sets of hashes
* are already reasonably distributed (so don't benefit from
* spreading), and because we use trees to handle large sets of
* collisions in bins, we just XOR some shifted bits in the
* cheapest possible way to reduce systematic lossage, as well as
* to incorporate impact of the highest bits that would otherwise
* never be used in index calculations because of table bounds.
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; //计算出的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;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
LinkedHashMap
- 作为HashMap的子类, 由双向链表实现, 定义了迭代顺序.
HashTable
- HashMap和HashTable都是Hash表
- HashTable是线程安全的, 而HashMap不是, HashTable, 不允许
HashTable 添加操作分析
private transient Entry<?,?>[] table; //HashTable中的键值对存放在各个存储桶中, 存储桶为Entry<k,v>数组
/**
* The table is rehashed when its size exceeds this threshold. (The
* value of this field is (int)(capacity * loadFactor).)
*
* @serial
*/
private int threshold;
/**
* Maps the specified <code>key</code> to the specified
* <code>value</code> in this hashtable. Neither the key nor the
* value can be <code>null</code>. <p>
*
* The value can be retrieved by calling the <code>get</code> method
* with a key that is equal to the original key.
*
* @param key the hashtable key
* @param value the value
* @return the previous value of the specified key in this hashtable,
* or <code>null</code> if it did not have one
* @exception NullPointerException if the key or value is
* <code>null</code>
* @see Object#equals(Object)
* @see #get(Object)
*/
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) { //要求value不为空
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length; //计算存储桶位置
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index]; //取出存储桶
for(; entry != null ; entry = entry.next) { //遍历该存储桶查找是否key已经存在
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index); //若不存在则进行添加
return null;
}
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
if (count >= threshold) { //若存储数量达到临界值
// Rehash the table if the threshold is exceeded
rehash(); //则进行扩容和table的再散列
tab = table; //再散列后引用地址改变需要重新赋值
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length; //重新计算存储桶位置
}
// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index]; //找到该存储桶
tab[index] = new Entry<>(hash, key, value, e); //添加在存储桶头部(e==Entry<K,V> next;)
count++;
}
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1; //
if (newCapacity - MAX_ARRAY_SIZE > 0) { //判断是否已经到最大了
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); //返回扩容后的大小和边界大小(最大大小)小的那个
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;) { //原有内容进行再散列
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
Entry<K,V>
/**
* Hashtable bucket collision list entry
*/
private static class Entry<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Entry<K,V> next;
protected Entry(int hash, K key, V value, Entry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
@SuppressWarnings("unchecked")
protected Object clone() {
return new Entry<>(hash, key, value,
(next==null ? null : (Entry<K,V>) next.clone()));
}
// Map.Entry Ops
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V value) {
if (value == null)
throw new NullPointerException();
V oldValue = this.value;
this.value = value;
return oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
(value==null ? e.getValue()==null : value.equals(e.getValue()));
}
public int hashCode() {
return hash ^ Objects.hashCode(value);
}
public String toString() {
return key.toString()+"="+value.toString();
}
}
WeekHashMap
- 这个数据结构将和垃圾回收器协同工作, 一起删除键/值对
TreeMap
- TreeMap的排序使用红黑数实现.
- 可以根据键的自然顺序进行排序也可以根据创建者提供的Comparator进行排序.
private final Comparator<? super K> comparator;
private transient Entry<K,V> root;