java 容器大小使用调优

这篇博客探讨了Java中ArrayList和HashMap的初始化大小对其性能的影响。默认构造函数可能导致不必要的容量扩展和元素拷贝,建议在创建容器时预估元素数量并指定初始容量,以避免性能损失。
摘要由CSDN通过智能技术生成

         这是我在csdn的第一篇博文,轻拍。

         之所以写这篇文章,是因为发现身边很多人有了java api后就可以“如鱼得水”写java程序,运行起来一旦发现性能问题后,便开始研究复杂的jvm内存回收机制或者jvm原理,但其实对一些常用的接口进行深入的了解和优化使用后,就可以得到很大的收益。

        相信大家用得最多的容器就是list和map,list无外乎ArrayList和LinkedList。用得最多的用法就是:

         List list = new ArrayList();

         list.add(a);

         这样是没问题,因为大家很多考虑到的都是remove的时候性能是O(n),add的性能开销是O(1),但有些情况并非如此。我们看一下ArrayList的构造函数:
    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
    this(10);
    }

  public ArrayList(int initialCapacity) {
    super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
    this.elementData = new Object[initialCapacity];
    }

   两个构造函数,第一个默认的初始大小是10,第二个是按设置的大小进行资源申请,当需要的容器大小超过capacity时,看看java怎么做:

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

     public void ensureCapacity(int minCapacity) {
    modCount++;
    int oldCapacity = elementData.length;
    if (minCapacity > oldCapacity) {
        Object oldData[] = elementData;
        int newCapacity = (oldCapacity * 3)/2 + 1;
            if (newCapacity < minCapacity)
        newCapacity = minCapacity;
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
    }
    }

 看出来了吧,当资源不够的时候,会首先申请原capacity*1.5大小的容器,然后进行所有值拷贝,再添加新元素,性能开销岂止O(n)啊,当元素个数达到一定程度时,建议养成一个良好习惯,在已知元素个数的时候,顺手传一个值进去。

    说了list,大家肯定会想到map,map有这个问题吗,我们以hashmap讨论,hashmap的结构是索引加链表(每个索引下一个链表),从代码里面看,发现和ArrayList实现原理一样:

     public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    /**
     * Constructs an empty <tt>HashMap</tt> with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
        table = new Entry[DEFAULT_INITIAL_CAPACITY];
        init();
    }

  先默认申请的是大小为16的map,阈值为0.75.然后进行put操作时:

    public V put(K key, V value) {
       /*

           检查是否有同一key的项,如果有,进行value覆盖

        */
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

   允许我用汉字来替代一些代码,如果发现是新增项,便开始添加了,

    void addEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        if (size++ >= threshold)
            resize(2 * table.length);
    }

    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }

  你看看,和ArrayList一模一样,唯一不同的是,这里会重新申请原大小2倍的hashmap,然后这里的transfer是将旧map中的元素按新map的大小,进行重新一个个添加,多麻烦。。。

  好了,先说到这里了,不知道有什么说得不当的地方,请指正!

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值