Java集合 -- ArrayList 的扩容机制HashMap 和 Hashtable的区别HashMap 和 HashSet区别HashSet如何检查重复
Posted CodeJiao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java集合 -- ArrayList 的扩容机制HashMap 和 Hashtable的区别HashMap 和 HashSet区别HashSet如何检查重复相关的知识,希望对你有一定的参考价值。
1. ArrayList 的扩容机制
- 默认容量大小:private static final int DEFAULT_CAPACITY = 10;
- 存放元素的数组:transient Object[] elementData;
- 实际元素个数:private int size;
创建对象时调用的无参构造函数:
//这是一个空的数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = ;
public ArrayList()
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
- 这段源码说明当你没有向集合中添加任何元素时,集合容量为0。那么默认的10个容量怎么来的呢?
这就得看看add方法的源码了:
add(E e)
public boolean add(E e)
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
- 假设你new了一个数组,当前容量为0,size当然也为0。这时调用add方法进入到ensureCapacityInternal(size + 1);
该方法源码如下:
ensureCapacityInternal(int minCapacity)
private void ensureCapacityInternal(int minCapacity)
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
- 该方法中的参数minCapacity传入的值为size+1也就是 1,接着我们再进入到calculateCapacity(elementData, minCapacity)里面:
calculateCapacity(Object[] elementData, int minCapacity)
private static int calculateCapacity(Object[] elementData, int minCapacity)
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
return minCapacity;
- 上文说过,elementData就是存放元素的数组,当前容量为0,if条件成立,返回默认容量DEFAULT_CAPACITY也就是10。这个值作为参数又传入ensureExplicitCapacity(int minCapacity)方法中,进入该方法查看源码:
ensureExplicitCapacity(int minCapacity)
private void ensureExplicitCapacity(int minCapacity)
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
- 我们先不要管modCount这个变量。
- 因为elementData数组长度为0,所以if条件成立,调用grow方法,重要的部分来了,我们再次进入到grow方法的源码中:
grow(int minCapacity)
private void grow(int minCapacity)
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
- 这个方法先声明了一个oldCapacity变量将数组长度赋给它,其值为0;又声明了一个newCapacity变量其值为oldCapacity+一个增量,可以发现这个增量是和原数组长度有关的量,当然在这里也为0。第一个if条件满足,newCapacity的值为10(这就是默认的容量,不理解的话再看看前面)。第二个if条件不成立,也可以不用注意,因为MAX_ARRAY_SIZE的定义如下:
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
这个值太大了以至于第二个if条件没有了解的必要。
-
最后一句话就是为elementData数组赋予了新的长度,Arrays.copyOf()方法返回的数组是新的数组对象,原数组对象不会改变,该拷贝不会影响原来的数组。copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值。
-
这时候再回到add的方法中,接着就向下执行elementData[size++] = e;到这里为止关于ArrayList就讲解得差不多了,当数组长度为10的时候你们可以试着过一下源码,查一下每次的增量是多少(答案是每次扩容为原来的1.5倍)。
2. HashMap 和 Hashtable的区别
- 线程是否安全:
HashMap
是非线程安全的,HashTable
是线程安全的;HashTable
内部的方法基本都经过synchronized
修饰。(如果你要保证线程安全的话就使用ConcurrentHashMap
吧!); - 效率:因为线程安全的问题,
HashMap
要比HashTable
效率高一点。另外,HashTable
基本被淘汰,不要在代码中使用它; - 对Null key 和Null value的支持:
HashMap
中,null
可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为null
。但是在HashTable
中put
进的键值只要有一个null
,直接抛出NullPointerException
。 初始容量大小和每次扩充容量大小的不同
:- 创建时如果不指定容量初始值,
Hashtable
默认的初始大小为11
,之后每次扩充,容量变为原来的2n+1
。HashMap
默认的初始化大小为16
。之后每次扩充,容量变为原来的2
倍。 - 创建时如果给定了容量初始值,那么
Hashtable
会直接使用你给定的大小,而HashMap
会将其扩充为2的幂次方大小(HashMap 中的tableSizeFor()
方法保证,下面给出了源代码)。也就是说HashMap
总是使用2
的幂作为哈希表的大小,后面会介绍到为什么是2
的幂次方。
- 创建时如果不指定容量初始值,
HashMap 中带有初始容量的构造函数:
下面这个方法保证了 HashMap
总是使用2的幂作为哈希表的大小。
3. HashMap 和 HashSet区别
如果你看过 HashSet
源码的话就应该知道:HashSet
底层就是基于 HashMap
实现的。HashSet
的源码非常非常少,因为除了 clone()
、writeObject()
、readObject()
是 HashSet
自己不得不实现之外,其他方法都是直接调用 HashMap
中的方法。
-
点进去HashSet的构造方法,我们发现HashSet就是用HashMap实现的:
-
点进去HashSet的put方法,发现就是用的map的put方法(用map的key来保存数据):
4. HashSet如何检查重复
当你把对象加入HashSet
时,HashSet会先计算对象的hashcode
值来判断对象加入的位置,同时也会与其他加入的对象的hashcode
值作比较,如果没有相符的hashcode
,HashSet
会假设对象没有重复出现。但是如果发现有相同hashcode
值的对象,这时会调用equals()
方法来检查hashcode
相等的对象是否真的相同。如果两者相同,HashSet
就不会让加入操作成功。
hashCode()与equals()的相关规定:
- 如果两个对象相等,则hashcode⼀定也是相同的。
- 两个对象相等,对两个equals⽅法返回true。
- 两个对象有相同的hashcode值,它们也不⼀定是相等的。
- 综上,equals方法被覆盖过,则hashCode⽅法也必须被覆盖(equals返回true,两个对象就一定相等。而两个对象相等,则hashcode⼀定也是相同的)。
- hashCode()的默认⾏为是对堆上的对象产⽣独特值。如果没有重写hashCode(),则该class的两个对象⽆论如何都不会相等(即使这两个对象指向相同的数据)。
==
与equals的区别:
==
是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同。==
是指对内存地址进行比较equals()
是对字符串的内容进行比较。==
指引用是否相同equals()
指的是值是否相同。
以上是关于Java集合 -- ArrayList 的扩容机制HashMap 和 Hashtable的区别HashMap 和 HashSet区别HashSet如何检查重复的主要内容,如果未能解决你的问题,请参考以下文章
Java集合 -- ArrayList 的扩容机制HashMap 和 Hashtable的区别HashMap 和 HashSet区别HashSet如何检查重复
7-java安全基础——ArrayList,Vector源码分析
7-java安全基础——ArrayList,Vector源码分析