今天看到了HashMap的一种新的创建方式,观察其底层代码后,决定将其记录,并复习了一下HashMap的相关知识。
HashMap作为一种常用的数据结构,通常情况下我们通过前两种方法对其进行创建。今天看到了第三种创建方式。
int capacity = 8;
HashMap<String, String> map1 = new HashMap<>();
HashMap<String, String> map2 = new HashMap<>(capacity);
HashMap<String, String> map3 = Maps.newHashMapWithExpected(capacity);
new HashMap<>()
第一种map1
的创建方式通常不做推荐,没有设置容量大小,在创建时会采用HashMap的默认大小16(由HashMap类中DEFAULT_INITIAL_CAPACITY 指定)。
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
该方法由于没有指定容量大小,实际容量小时造成空间浪费。实际容量大,则会频繁扩容,耗费时间。
new HashMap<>(capacity)
第二种map2
的创建方式被经常使用,会根据实际应用场景来设置所创建的HashMap容量大小。但是观察底层代码发现,实际创建的HashMap容量大小并非所设置的值。
/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and the default load factor (0.75).
*
* @param initialCapacity the initial capacity.
* @throws IllegalArgumentException if the initial capacity is negative.
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
/**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
可以看到,指定容量创建HashMap,代码会对参数进行相关判断,此时扩容因子为默认的0.75。然后将容量(threshold )设置成给定目标容量的两次幂。这里解释两个地方:
- 给定目标容量的两次幂:大于等于给定值的最小的二次幂(2n)。比如:传入7,返回8(23);传入8,返回8(23);传入9,返回16(24)
- threshold:当HashMap的size大于threshold时会执行resize操作
Maps.newHashMapWithExpected(capacity)
今天看到了map3
所示的HashMap创建方法。该方法由google提供的guava包所提供
<!-- google java lib -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>17.0</version>
</dependency>
其底层代码如下:文章来源:https://www.toymoban.com/news/detail-690094.html
//@return a new, empty {@code HashMap} with enough capacity to hold {@code expectedSize} entries without resizing
public static <K, V> HashMap<K, V> newHashMapWithExpectedSize(int expectedSize) {
return new HashMap<K, V>(capacity(expectedSize));
}
static int capacity(int expectedSize) {
if (expectedSize < 3) {
checkNonnegative(expectedSize, "expectedSize");
return expectedSize + 1;
}
if (expectedSize < Ints.MAX_POWER_OF_TWO) { //MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2);
// This is the calculation used in JDK8 to resize when a putAll
// happens; it seems to be the most conservative(保守的) calculation we
// can make. 0.75 is the default load factor.
return (int) ((float) expectedSize / 0.75F + 1.0F);
}
return Integer.MAX_VALUE; // any large value //MAX_VALUE = 0x7fffffff;
可以看到,通过该方法创建HashMap,创建的大小是 expectedSize / 0.75F + 1.0F
,和方法二最大的区别就是其对传入的容量大小进行计算,将得到的结果设置给capacity。
复习了一下HashMap的知识就可以知道这样做的好处。因为HashMap的resize操作触发时机,由容量大小和扩容因子共同决定。当HashMap的size大于threshold时进行resize操作。threshold为capacity*loadFactor
,而loadFactor默认是0.75。因此,假设我们预计HashMap共有32个数据,则将其容量设置成32/0.75+1
,可以使得size=32 < threshold = capacity * loadFactor = ( 32 / 0.75 + 1 ) * 0.75。在整个过程中,避免HashMap进行resize操作。文章来源地址https://www.toymoban.com/news/detail-690094.html
到了这里,关于创建HashMap三种方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!