这是我在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的大小,进行重新一个个添加,多麻烦。。。
好了,先说到这里了,不知道有什么说得不当的地方,请指正!