创建HashMap三种方式

9 篇文章 0 订阅

今天看到了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>

其底层代码如下:


//@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操作。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值