ArrayList、HashMap、HashSet源码总结
ArrayList:
1. ArrayList是List接口的大小可变数组的实现,此实现是不同步的。
2. ArrayList内部使用类型为Object[]的数组存储元素。
3. ArrayList默认的数组长度为10, 当需要扩大容量时,扩大后的容量为:newCapacity = (oldCapacity * 3)/2 + 1;
4. ArrayList的clone方法为浅拷贝(shallow copy)
5. ArrayList的remove方法根据参数类型的不同有两种重载:
remove(int index) : 删除指定位置的元素;
remove(Object o) : 删除第一个遇到的元素,如果没有不做改变
6. ArrayList允许null值、允许重复值、不排序,获取快速,增删麻烦。
HashMap:
HashMap是不同步的。
HashMap内部使用类型为Entry[]的数组存储元素。Entry是HashMap的一个内部类,定义如下所示。
每一个Entry对象其实是一个单向链表,之后的解析可以看到,最后存入的元素在最前面。
备注:下面出现的代码都是HashMap.java中的源码,中文描述是作者加的。
transient Entry[] table;//HashMap内部定义的数据存储变量
//内部类
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
*****
省略
*****
}
HashMap中几个概念:
capacity:容量,即Entry[]数组的长度
loadFactor:负载因子,Entry[]数组中实际数据量/容量的比例达到loadFactor时,HashMap就需要扩大容量了,一般扩大为原来的两倍。
threshold: 当HashMap中的元素个数超过这个数值时,就将扩大容量。
put方法:
public V put(K key, V value) {
if (key == null)
//如果key为null,特殊处理,key为null直接存储在table[0]位置。
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);//此处得到的i即为key对应的HashMap中的存储位置table[i]
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
//从Entry链表的第一个开始如果找到与key执行equals方法为true的Entry,则修改对应Entry的value值为新值,key不做修改
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
//如果没有找到对应的key,则执行增加操作
addEntry(hash, key, value, i);
return null;
}
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
//如果大小超过了threshold,扩大容量为原来的两倍。扩大容量时,所有的key-value需要重新hash。
resize(2 * table.length);
}
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);//将原来hash表中的数据放入新的hash表中,需要重新hash。
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}
/**
* Transfers all entries from current table to newTable.
*/
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
//此处使用循环,将原来hash链中的所有的key-value都重新获取hash值,重新放置。
//因为放置位置是跟hash表的大小有关的,当hash表容量扩大后,之前放在一个地方的key-value对现在可能hash不到同一个地方了。
do {
Entry<K,V> next = e.next;//记录此处的下一个地址
int i = indexFor(e.hash, newCapacity);//重新计算当前的key-value在新hash表中的位置
e.next = newTable[i];//将之前在同一位置的数据放在e的next位置,没有则为null
newTable[i] = e;//将e作为hash表i位置的第一个元素
e = next;//将next赋值给e, 对原来j位置的所有的元素都执行重新hash,重新放置
} while (e != null);
}
}
}
get方法:按照put时的逻辑根据key获取value。不再详述。
keySet与values方法:
这两个方法作用好理解,但需要注意的是,当对keySet()和values()方法获取到的集合执行remove操作的时候就相当于对HashMap集合本身执行remove操作。看源码通过keySet和values获取到的好像是HashMap的迭代器,这里我没有深究。如果谁明白具体原因不吝赐教。
HashSet:
HashSet的内部是用的HashMap实现的,使用Entry将每一个HashSet元素的引用存储在key位置,value位置使用默认的数据填充。
在此也可以看到,HashMap中的key-value对其实可以看成value是每一个key的附属,只需要找到每一个key的位置,然后把对应的value放入即可。
ArrayList、HashMap、HashSet源码总结的更多相关文章
- HashSet源码学习,基于HashMap实现
HashSet源码学习 一).Set集合的主要使用类 1). HashSet 基于对HashMap的封装 2). LinkedHashSet 基于对LinkedHashSet的封装 3). TreeS ...
- 【Java集合】HashSet源码解析以及HashSet与HashMap的区别
HashSet 前言 HashSet是一个不可重复且元素无序的集合.内部使用HashMap实现. 我们可以从HashSet源码的类注释中获取到如下信息: 底层基于HashMap实现,所以迭代过程中不能 ...
- Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- java基础解析系列(十)---ArrayList和LinkedList源码及使用分析
java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ...
- HashSet源码分析
在java集合中有一种集合Set(集),他有两个实现类,分别是HashSet,TreeSet.下面仔细分析HashSet源码. 看了HashSet的源码就会发现HashSet的底层实现是利用HashM ...
- HashSet源码
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java. ...
- 死磕Java之聊聊HashSet源码(基于JDK1.8)
HashSet的UML图 HashSet的成员变量及其含义 public class HashSet<E> extends AbstractSet<E> implements ...
- HashSet源码分析:JDK源码系列
1.简介 继续分析源码,上一篇文章把HashMap的分析完毕.本文开始分析HashSet简单的介绍一下. HashSet是一个无重复元素集合,内部使用HashMap实现,所以HashMap的特征耶继承 ...
- Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例
概要 上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具体实现类进行讲解:首先,讲解List,而List中ArrayList又最为常用.因此,本章我们讲解Arra ...
- 【转】Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例
原文网址:http://www.cnblogs.com/skywang12345/p/3308556.html 上一章,我们学习了Collection的架构.这一章开始,我们对Collection的具 ...
随机推荐
- [HNOI 2013]游走
Description 题库链接 一个无向连通图,顶点从 \(1\) 编号到 \(N\) ,边从 \(1\) 编号到 \(M\) . 小Z在该图上进行随机游走,初始时小Z在 \(1\) 号顶点,每一步 ...
- [HNOI 2015]开店
Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...
- [SDOI2016]储能表
Description 有一个 n 行 m 列的表格,行从 0 到 n−1 编号,列从 0 到 m−1 编号.每个格子都储存着能量.最初,第 i 行第 j 列的格子储存着 (i xor j) 点能量. ...
- [Sdoi2009]Elaxia的路线
Description 最近,Elaxia和w**的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们 必须合理地安排两个人在一起的时间.Elaxia和w**每天都要奔波于宿舍和实验室之间, ...
- ●洛谷P3168 [CQOI2015]任务查询系统
题链: https://www.luogu.org/problemnew/show/P3168题解: 主席树 强制在线? 那就直接对每一个前缀时间建一个线段树(可持久化线段树),线段树维护优先度权值. ...
- [bzoj4874]筐子放球
来自FallDream的博客,未经允许,请勿转载,谢谢. 小N最近在研究NP完全问题,小O看小N研究得热火朝天,便给他出了一道这样的题目: 有 n 个球,用整数 1 到 n 编号.还有 m 个筐子,用 ...
- java开发笔记——表映射实体类代码示例
package com.special.ipmsdm; import java.io.Serializable; import javax.persistence.Column; import jav ...
- C# 解决winform 窗体控件在窗体变化时闪烁的问题
在窗体form代码中加入如下代码即可: protected override CreateParams CreateParams { get { CreateParams cp = base.Crea ...
- java 第三次作业
(一)学习总结 1.阅读下面程序,分析是否能编译通过?如果不能,说明原因.应该如何修改?程序的运行结果是什么?为什么子类的构造方法在运行之前,必须调用父 类的构造方法?能不能反过来? class Gr ...
- STM8操作LCD5110总结
附上一小段代码: void LCD_init(void) { // 产生一个让LCD复位的低电平脉冲 //LCD_RST = 0; GPIO_WriteLow(LCD_PORTG, LCD_RST); ...