ArrayList,hashSet解析

本文深入解析了ArrayList的数据结构与核心方法实现,如add方法的工作原理,并详细介绍了HashSet如何利用HashMap来保证元素唯一性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ArrayList定义只定义类两个私有属性:

private transient Object[] elementData;//elementData存储ArrayList内的元素
private int size;//size表示它包含的元素的数量
 Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。

ArrayList的构造函数:

第一个构造方法使用提供的initialCapacity来初始化elementData数组的大小。

第二个构造方法调用第一个构造方法并传入参数10,即默认elementData数组的大小为10。

第三个构造方法则将提供的集合转成数组返回给elementData(返回若不是Object[]将调用Arrays.copyOf方法将其转为Object[])。


add(E e)

    add(E e)都知道是在尾部添加一个元素,如何实现的呢?

public boolean add(E e) {
    ensureCapacity(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
    }

看到add(E e)中先调用了ensureCapacity(size+1)方法,之后将元素的索引赋给elementData[size],而后size自增。例如初次添加时,size为0,add将elementData[0]赋值为e,然后size设置为1(类似执行以下两条语句elementData[0]=e;size=1)。将元素的索引赋给elementData[size]不是会出现数组越界的情况吗?这里关键就在ensureCapacity(size+1)中了。

    根据ensureCapacity的方法名可以知道是确保容量用的。ensureCapacity(size+1)后面的注释可以明白是增加modCount的值(加了俩感叹号,应该蛮重要的,来看看)。

 public void ensureCapacity(int minCapacity) {
 9     modCount++;
10     int oldCapacity = elementData.length;
11     if (minCapacity > oldCapacity) {
12         Object oldData[] = elementData;
13         int newCapacity = (oldCapacity * 3)/2 + 1;
14             if (newCapacity < minCapacity)
15         newCapacity = minCapacity;
16             // minCapacity is usually close to size, so this is a win:
17             elementData = Arrays.copyOf(elementData, newCapacity);
18     }
19     }
复制代码

 The number of times this list has been structurally modified.

    这是对modCount的解释,意为记录list结构被改变的次数(观察源码可以发现每次调用ensureCapacoty方法,modCount的值都将增加,但未必数组结构会改变,所以感觉对modCount的解释不是很到位)。

    增加modCount之后,判断minCapacity(即size+1)是否大于oldCapacity(即elementData.length),若大于,则调整容量为max((oldCapacity*3)/2+1,minCapacity),调整elementData容量为新的容量,即将返回一个内容为原数组元素,大小为新容量的数组赋给elementData;否则不做操作。

    所以调用ensureCapacity至少将elementData的容量增加的1,所以elementData[size]不会出现越界的情况。

 add(int index, E element)

public void add(int index, E element) {
 2     if (index > size || index < 0)
 3         throw new IndexOutOfBoundsException(
 4         "Index: "+index+", Size: "+size);
 5 
 6     ensureCapacity(size+1);  // Increments modCount!!
 7     System.arraycopy(elementData, index, elementData, index + 1,
 8              size - index);
 9     elementData[index] = element;
10     size++;
11     }

 首先判断指定位置index是否超出elementData的界限,之后调用ensureCapacity调整容量(若容量足够则不会拓展),调用System.arraycopy将elementData从index开始的size-index个元素复制到index+1至size+1的位置(即index开始的元素都向后移动一个位置),然后将index位置的值指向element。


HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用null元素。

对于HashSet而言,它是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet 的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成。

底层实际将将该元素作为key放入HashMap。 
120     * 由于HashMap的put()方法添加key-value对时,当新放入HashMap的Entry中key 
121     * 与集合中原有Entry的key相同(hashCode()返回值相等,通过equals比较也返回true), 
122     * 新添加的Entry的value会将覆盖原来Entry的value,但key不会有任何改变, 
123     * 因此如果向HashSet中添加一个已经存在的元素时,新添加的集合元素将不会被放入HashMap中, 
124     * 原来的元素也不会有任何改变,这也就满足了Set中元素不重复的特性。 
125     * @param e 将添加到此set中的元素。 
126     * @return 如果此set尚未包含指定元素,则返回true。
public boolean add(E e) {  
129    return map.put(e, PRESENT)==null;  
130    }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值