java基础——集合源码分析

java基础


java集合源码分析,对HashMap一步步进行debug分析。至于面试,集合的其他细节另外开一篇



一、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)扩容源码

  1. 下面这个方法就添加数据到 vector 集合
//下面这个方法就添加数据到 vector 集合 
public synchronized boolean add(E e) { 
	modCount++; 
	ensureCapacityHelper(elementCount + 1);
	elementData[elementCount++] = e; 
	return true; 
}
  1. 确定是否需要扩容 条件 : minCapacity - elementData.length>0
//确定是否需要扩容 条件 : minCapacity - elementData.length>0 
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code 
	if (minCapacity - elementData.length > 0) 
 	grow(minCapacity); 
 }
  1. 如果 需要的数组大小 不够用,就扩容 , 扩容的算法,扩容两倍
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,就会扩容到16
2=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对象的所有旧值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值