本文发表于本人博客

上次说了下HashSet和HashMap之间的关系,其中HashMap这个内部有这么一句:

static final float DEFAULT_LOAD_FACTOR = 0.75f;

这一句表示一个常量,作用是当容器数量大小达到0.75%的时候就进行重新在构建一个2倍大小的数组。竟然这2个是集合,那我们今天看看其他的集合类,比如:ArrayList、Vector、LinkedList,开始咯。

首先查看下ArrayList的源码,这几个集合类都是在java.util包下;看其构造:

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

可以看到当我们new ArrayList()的时候,其实质在内部是默认初始化一个长度为10的Object对象数组。明白了内部是用数组来实现的;继续看下那如果我们add的时候又是怎么样的呢?看看其代码:

    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;
elementData = Arrays.copyOf(elementData, newCapacity);
}
}

这里可以看出,add对象的时候先要去判断插入的位置小标是否大于数组的长度,是的话就重新实例化一个数组赋值给elementData,重新实例化数组的时候长度时按照当前长度*3 / 2 +1来计算,那就是每次add的时候都会判断,如果一次性插入很多(几百几千上万)元素对象的话,那我们可以想象一下,10个元素的时候就会重新实例化一个数组,在16个时候又一次,在25的时候又一次,以此算下去这样的性能应该是太差了,所以我们在知道数组长度的情况可以直接调用带参数构造来提升一下性能如:

ArrayList list = new ArrayList(10000);

那接下来我们看看其查询方法:

    public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}

可以看出查询的时候是遍历整个数组的!在看下移除代码:

    public E remove(int index) {
RangeCheck(index);
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,numMoved);
elementData[--size] = null;
return oldValue;
}
private void RangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size);
}

这移除的代码也是先要判断小标是否超出了,不然就截取数组。从这写方面我们可以看出:ArrayList在查找的时候直接使用的是下标遍历,增加删除的时候是需要重新构造一个新的数组性能消耗比较大!


现在来看下Vector这个类,先看其构造:

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

可以看出这个Vector内部也是使用的是Object数组来实现的,initialCapacity我们可以看出是设置的数组长度,而capacityIncrement是什么呢,我们看看其注释:

    /**
* The amount by which the capacity of the vector is automatically
* incremented when its size becomes greater than its capacity. If
* the capacity increment is less than or equal to zero, the capacity
* of the vector is doubled each time it needs to grow.
*
* @serial
*/

我靠英文不好,奇葩了,有点难我们看使用的地方:

    private void ensureCapacityHelper(int minCapacity) {
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object[] oldData = elementData;
int newCapacity = (capacityIncrement > 0) ?(oldCapacity + capacityIncrement) : (oldCapacity * 2);
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
elementData = Arrays.copyOf(elementData, newCapacity);
}
}

从这个代码中可以看到当设置了capacityIncrement>0时,增加对象的时候会在重新构造新数组的时候作为新数组的长度,否则就是原来数组长度的2倍。OK!兄弟英文不好,苦逼啊!

下面来看看其的增加删除是怎么实现的。看代码:

    public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
Object oldValue = elementData[index];
int numMoved = elementCount - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--elementCount] = null;
return (E)oldValue;
}

呀,这里出现了synchronized关键字了,是同步管理哦。增加Add的时候先去判断是否需要重新构造新的数组,然后赋值;删除的时候先判断下标是否超出,否则直接截取数组,这个跟ArrayList一样!到此可以看出Vector内部也是使用的是数组来实现,但是在增加删除有些方法的时候使用同步即使在多线程下也能确保正确。这个的性能会比ArrayList稍点,但是如果考虑到多线程环境下应该使用这个确保查询正确执行。


接下来我们看看LinkedList这个,看下其代码:

public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable

从这个可以看到,这个LinkedList竟然实现了Deque哦,可以看看这个的注释,是双向循环的链表。那竟然是链表大家应该知道了,链表在插入数据删除数据的时候会不数组的要快,原因是不用去移除数据直接来弄再修改指针就可以了,但是也有不好的地方就是查询啊,是要先从当前节点的位置从下或网上依次查找下次找个比较费时!

public boolean add(E e) {
addBefore(e, header);
return true;
}
private Entry<E> addBefore(E e, Entry<E> entry) {
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
private E remove(Entry<E> e) {
if (e == header)
throw new NoSuchElementException();
E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next = e.previous = null;
e.element = null;
size--;
modCount++;
return result;
}

从上面代码可以看出,增加删除都只是对header的节点进行操作就可以了,这样大大提高性能。

    private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0;
public LinkedList() {
header.next = header.previous = header;
} private static class Entry<E> {
E element;
Entry<E> next;
Entry<E> previous;
......
......
}

从这部分代码可以看出使用了header来做链接节点,找个header有previous以及next节点,一前一后构成了整个链表循环。

这次先到这里。坚持记录点点滴滴!

Java基础知识陷阱(七)的更多相关文章

  1. Java基础知识陷阱系列

    Java基础知识陷阱系列 今天抽空把Java基础知识陷阱有关的文章汇总于此,便于大家查看. Java基础知识陷阱(一) Java基础知识陷阱(二) Java基础知识陷阱(三) Java基础知识陷阱(四 ...

  2. Java基础知识陷阱(九)

    本文发表于本人博客. 今天我来说说关于JAVA多线程知识,有错误请指出.大家都知道JAVA在服务端上处理也有很大优势,很多公司也有在服务器跑JAVA进程,这说明JAVA在处理这个多线程以及并发下也有一 ...

  3. Java基础知识陷阱(十)

    本文发表于本人博客. 上个星期由于时间比较紧所以未能继续写下去,今天再接再厉,专心 + 坚持这样离目标就越来越近了!废话少说说正题,今天我们还是来说说java中比较基础的知识,大家知道编写java程序 ...

  4. Java基础知识陷阱(二)

    本文发表于本人博客. 上次说了一些关于字符串的知识,都是比较基础的,那这次也说下关于对象地址问题,比如传参.先看下面代码: public void changeInt(int a){ a = ; } ...

  5. Java基础知识陷阱(六)

    本文发表于本人博客. 上次说了下equals跟==的问题,今天再来认识一下这个equals()跟hasCode().上次的代码如下: class Person{ public String name; ...

  6. Java基础知识陷阱(四)

    本文发表于本人博客. 今天我们来说说关于java继承以及反射有关的问题,大家先看下下面代码,试问可以编译通过不,为什么具体说说原因? public class Test{ public static ...

  7. Java基础知识陷阱(三)

    本文发表于本人博客. 之前都讲了有关字符串的陷阱,那今天来说下关于静态这个东西,这分为静态变量.静态方法,先看下面的代码请问结果输出是什么?: class Person01{ private stat ...

  8. Java基础知识陷阱(一)

    本文发表于本人博客. 事隔好多年了,重新拿起来Java这门语言,看似熟悉其实还很陌生,想想应该梳理下顺便提高下自己.这次先来看看Java里面的String这个对象. 经典的先看下面一段代码,请问最终创 ...

  9. JAVA基础知识(七)存根类

    存根类是一个类,它实现了一个接口,它的作用是:如果一个接口有很多方法,如果要实现这个接口,就要实现所有的方法.但是一个类从业务来说,可能只需要其中一两个方法.如果直接去实现这个接口,除了实现所需的方法 ...

随机推荐

  1. 负margin应用案例几则(转载+总结)

    (一)自适应布局——左栏改右栏 这里先写个一列固定列宽,另一列自适应的两列布局,效果图: 侧栏移至右边,效果图: 其HTML <div class="wrap"> &l ...

  2. OSG 中 相交測试 模块 工作流程及原理

    主要涉及三个类: 1. osgUtil::PolytopeIntersector // 详细不同算法实现类 2. osgUtil::IntersectionVisitor //用来遍历节点树的每一个节 ...

  3. redhat6.2 clang编译环境搭建(采用源码包编译安装)

    1. About clang++ office site:http://clang.llvm.org/ A major focus of our work on clang is to make it ...

  4. LR的响应时间与使用IE所感受时间不一致的讨论(摘抄补充)

    http://www.51testing.com/html/33/564333-865629.html 在做性能测试时,有时碰到这样一种情况,使用性能工具LR测试出来的响应时间比实际使用IE感受到的时 ...

  5. 深入分析jquery解析json数据

    我们先以解析上例中的comments对象的JSON数据为例,然后再小结jQuery中解析JSON数据的方法. JSON数据如下,是一个嵌套JSON: {"comments":[{& ...

  6. 隐马尔可夫树(HMT模型)

    HMT(Hidden Markov Tree)隐马尔可夫树 [论文] 小波变换与HMT模型的图像插值算法-郭昌-中山大学学报(自然科学版)

  7. loadimage1();有问题

    f.Read(pBuffer, nSize)不能少,少了虽然能读进去数据但是不能显示成图片,可能原因是存进的数据并不是图片数据! 输入图片测试,但是没有CFile先Open再Read

  8. 解决ScrollView滑动RecyclerView的卡顿

    我们不的不了解ViewConfiguration这个类,官方是这么解释的Contains methods to standard constants used in the UI for timeou ...

  9. c++11 lambda(了解)

    this->send_change_equip = ([this](ChangeEquipPT channge) { send_cmd(s2c_change_equip, &channg ...

  10. spring的@Transactional注解详细用法(转载)

    概述 事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性.Spring Framework对事务管理提供了一致的抽象,其特点如下: 为不同的事务API提供一致的编程模型, ...