java基础
java集合源码分析,对HashMap一步步进行debug分析。至于面试,集合的其他细节另外开一篇
文章目录
- java基础
- 一、List
- 1、ArrayList底层操作机制
- 2、Vector底层结构和机制
- 3、Vector 和 ArrayList 的比较
- 4、LinkList底层机制
- 5、ArrayList 和 LinkedList 的比较
- 二、Set
- 1、HashSet底层机制
- 2、LinkedHashSet底层机制
- 三、Map
- 1、JDK8Map接口实现类的特点
- 2、Map遍历方式
- 3、HashTable机制
- 3、Properties
- 四、总结-开发中如何选择集合实现类
- 五、TreeSet和TreeMap
- 1、TreeSet底层机制
- 2、TreeMap底层机制
- 六、Collections 工具类
- 1、Collections 工具类介绍
- 2、排序操作:(均为 static 方法)
- 3、查找替换:(均为 static 方法)
一、List
1、ArrayList底层操作机制
(1)ArrayList中维护了一个Object类型数组——就是说可以存储不同类型的对象
(2)当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需再次扩容,则扩容elementData为1.5倍——本身加右移一位
(3)如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。
(4)内部还维护了一个modConter++来记录集合被修改的次数
(5)创建完数组后用Arrays.copyOf()转移数据
(6)ArrayList可以加入多个null
2、Vector底层结构和机制
(1)Vector类的定义说明
public class Vector <E>
extends AbstractList<E>
implements List <E>, RandomAccess,Cloneable,Serializable
Serializable可序列化网络传输
(2)Vector底层也是一个数组对象,protected Objec[] elementData;
(3)Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArraylndexOutOfBoundsException(index);
return elementData(index);
}
(4)ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高)。在多线程情况下,不建议使用ArrayList;在开发中,需要考虑线程同步安全时,考虑使用Vector
(5)扩容源码
- 下面这个方法就添加数据到 vector 集合
//下面这个方法就添加数据到 vector 集合
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
- 确定是否需要扩容 条件 : minCapacity - elementData.length>0
//确定是否需要扩容 条件 : minCapacity - elementData.length>0
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
- 如果 需要的数组大小 不够用,就扩容 , 扩容的算法,扩容两倍
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容两倍
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
3、Vector 和 ArrayList 的比较
4、LinkList底层机制
(1)LinkList底层实现了双向链表和双端队列的特点
(2)可以添加任意元素,可以重复,包括null
(3)线程不安全,没有实现同步
(4)LinkList底层维护了一个双向循环链表
(5)LinkList中维护了两个属性first和last分别指向首节点和尾节点
(6)每个节点(Node对象),里面又维护了prev,next,item三个属性
5、ArrayList 和 LinkedList 的比较
二、Set
1、HashSet底层机制
1.1 Hashset使用说明
(1)HashSet实际上是HashMap
(2)元素无序,不允许重复
(3)可以存放null,只能一个
(4)HashSet不保证元素是有序的,取决于Hash后,在确定索引的结果
1.2 HashMap底层添加元素实现
(1)HashSet底层是HashMap
(2)添加一个元素时,先得到hash值-会转成->索引值
(3)0找到存储数据表table,看这个索引位置是否已经存放的有元素
(4)如果没有,直接加入
(5)如果有,调用equals 比较,如果相同,就放弃添加,如果不相同,则添加到最后
(6)在Java8中,如果一条链表的元素个数到达 TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN TREEIFY CAPACITY(默认64),就会进行树化(红黑树)
1.3 HashMap扩容和转换红黑树机制
(1)HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值(threshold)是16加载因子(loadFactor)是0.75 = 12
(2)如果table数组使用到了临界值12,就会扩容到162=32,新的临界值就是32*0.75 =24,依次类推
(3)在Java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8).并且table的大小>=MIN TREEIFY CAPACITY(默认64),就会进行树化(红黑树),否则仍然采用数组扩容机制
1.4 HashMap更详细的分析
HashMap更详细的分析,看另外一篇文章:
https://blog.csdn.net/qq_43241387/article/details/120636772
2、LinkedHashSet底层机制
2.1LinkedHashSet全面说明
(1)LinkedHashSet是HashSet的子类
(2)LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双链表(head+tail)
(3)每一个节点有before和after属性,这样可以形成双向链表
(4)LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以顺序保存的
(5)LinkedHashSet不允许添加重复元素
(6)在添加一个元素时,先求hash值,在求索引.,确定该元素在table的位置,然后将添加的元素加入到双向链表(如果已经存在。不添加[原则和hashset一样])
tail.next = newElement ;//示意代码
newElement.pre = tail;
tail = newEelment;
(7)这样的话,我们遍历LinkedHashSet 也能确保插入顺序和遍
历顺序一致
2.2LinkedHashSet底层机制
(1)由于LinkedHashMap是HashMap的子类,所以LinkedHashMap自然会拥有HashMap的所有特性。比如,LinkedHashMap也最多只允许一条Entry的键为Null(多条会覆盖),但允许多条Entry的值为Null。
(2)准确地说,它是一个将所有Entry节点链入一个双向链表双向链表的HashMap。
(3)线程不安全。
(4)底层使用双向链表,可以保存元素的插入顺序,顺序有两种方式:一种是按照插入顺序排序,一种按照访问做排序。默认以插入顺序排序。
(5)源码解析:
Entry继承了Node多加了前后指向,维护包含所有节点的双链表,其他大同小异,添加方法还是调用的HashMap
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
HashMap与LinkedHashMap的Entry节点结构示意图:
三、Map
1、JDK8Map接口实现类的特点
(1)Map与Collection并列存在。用于保存具有映射关系的数据(双列元素):Key-Value,
(2)Map中的key和 value可以是任何引用类型的数据,会封装到HashMap$Node对象中
(3)Map中的key 不允许重复,原因和HashSet一样//前面分析过源码
(4)Map中的value可以重复
(5)Map 的key可以为null, value也可以为null,注意key为null, 只能有一个,value为null ,可以多个;当有相同的key时相当于替换。
(6)常用String类作为Map的key
(7)key 和 value之间存在单向一对一关系,即通过指定的key 总能找到对应的value
(8)Map存放数据的key—value示意图,一对k-v是放在一个HashMap $ Node中的,又因为Node实现了Entry接口,有些书上也说一对k-v就是一个Entry。
- k-v最后是HashMap $ Node node =newNode (hash, key, value, null)
- k-v为了方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素的类型Entry,而一个Entry对象就有k,v,EntrySet<Entry<k,v>>
- EntrySet是HashMap的内部类
- 在EntrySet内部是一组组引用,没存放真正的元素;它里面定义的类型是Map.set,但实际上存放的还是HashMap$Node ,Node实现了Entry的接口
- static class Node<k,v> implements Map.Entry<k,v>//所以那个node能放进entry
- 把node对象存放进entryset,是为了方便遍历,主要是因为Map.Entry里面有两个重要的方法,getkey,getvalue,方便使用
- 解释清楚了底层node怎么组织的,方便管理使用。
2、Map遍历方式
containsKey:查找键是否存在
keySet:获取所有的键
entrySet:获取所有关系 k-v
values:获取所有的值
(1)第一组: 先取出 所有的 Key , 通过 Key 取出对应的 Value
Set keyset = map.keySet();
//(1) 增强 for
System.out.println("-----第一种方式-------");
for (Object key : keyset) {
System.out.println(key + “-” + map.get(key));
}
//(2) 迭代器
System.out.println("----第二种方式--------");
Iterator iterator =keyset.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + “-” + map.get(key));
}
(2)第二组: 把所有的 values 取出
Collection values = map.values();
//这里可以使用所有的 Collections 使用的遍历方法
//(1) 增强 for
System.out.println("—取出所有的 value 增强 for----");
for (Object value : values) {
System.out.println(value);
}
//(2) 迭代器
System.out.println("—取出所有的 value 迭代器----");
Iterator iterator2 = values.iterator();
while (iterator2.hasNext()) {
Object value = iterator2.next();
System.out.println(value);
}
(3)第三组: 通过 EntrySet 来获取 k-v
Set entrySet = map.entrySet();// EntrySet<Map.Entry<K,V>>
//(1) 增强 for
System.out.println("----使用 EntrySet 的 for 增强(第 3 种)----");
for (Object entry : entrySet) {
//将 entry 转成 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + “-” + m.getValue());
}
//(2) 迭代器
System.out.println("----使用 EntrySet 的 迭代器(第 4 种)----");
Iterator iterator3 = entrySet.iterator();
while (iterator3.hasNext()) {
Object entry = iterator3.next();
//System.out.println(next.getClass());
//HashMap$Node -实现->Map.Entry (getKey,getValue)
//向下转型 Map.Entry
Map.Entry m = (Map.Entry)>entry;
System.out.println(m.getKey() + “-” + m.getValue());
}
3、HashTable机制
1、HashTable全面说明
(1)存放的元素是键值对:即K-V
(2) hashtable的键和值都不能为null,否则会抛出NullPointerException
(3)hashTable使用方法基本上和HashMap一样
(4)hashTable是线程安全的(synchronized), hashMap是线程不安全的
(5)扩容为2倍加1
2、Hastable和HashMap比较
3、Properties
1、Properties基本介绍
(1) Properties类继承自Hashtable类并且实现了Map接口,也是使用
种键值对的形式来保存数据。
(2)他的使用特点和Hashtable类似
(3)Properties还可以用于从 xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改
(4)说明:工作后Xxx.properties文件通常作为配置文件,这个知识点在IO流举例,有兴趣可先看文章
(5)Properties 继承 Hashtable,可以通过 k-v 存放数据,当然 key 和 value 不能为 null
四、总结-开发中如何选择集合实现类
五、TreeSet和TreeMap
1、TreeSet底层机制
(1)TreeSet底层机制是TreeMap
(2)TreeSet调用无参构造器,仍然是无序的
(3)使用 TreeSet 提供的一个构造器,可以传入一个比较器(匿名内部类) 并指定排序规则,我们在匿名内部类里面写比较规则(我们来决定而不是equals)。排序和添加都坏根据这个自定义的规则来。
(4)cpr是我们的匿名内部类传进来的对象。会动态绑定到我们匿名内部类。底层仍然是进入到TreeMap
2、TreeMap底层机制
(1)使用默认的构造器,创建 TreeMap, 是无序的(也没有排序)
(2)TreeMap里面的构造器,有一个实现了Comparator接口的构造器,从而进行排序
(3)构造器,把传入的实现了 Comparator 接口的匿名内部类(对象),传给给 TreeMap 的 comparator
(4)Entry也是TreeMap的内部类
(5)调用put方法,添加的时候第一次,直接加,第二次因为已经有元素了,所以会启用比较器,比较不相等直接返回不会替换。
六、Collections 工具类
1、Collections 工具类介绍
(1)Collections是一个操作 Set、 List 和 Map等集合的工具类
(2)Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作
2、排序操作:(均为 static 方法)
(1)reverse(List):反转 List中元素的顺序
(2)shuffle(List):对List集合元素进行随机排序
(3) sort(List):根据元素的自然顺序对指定List 集合元素按升序排序
(4)sort(List, Comparator):根据指定的 Comparator 产生的顺序对 List集合元素进行排序
(5) swap(List,int,int):将指定 list集合中的 i 处元素和 j 处元素进行交换
3、查找替换:(均为 static 方法)
(1)Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
(2) Object max(Collection, Comparator):根据Comparator 指定的顺序,返回给定集合中的最大元素
(3)Object min(Collection)
(4)Object min(Collection,Comparator)
(5) int frequency(Collection,Object):返回指定集合中指定元素的出现次数
(6) void copy(List dest,List src):将src中的内容复制到dest中
(7)boolean replaceAll(List list,Object oldVal, Object newVal):使用新值,替换List对象的所有旧值