本文发表于本人博客

上次说了下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. mysql_ado的demo

    winform程序 http://pan.baidu.com/s/1nvxm5br

  2. python 获取网页编码格式

    f = urllib2.urlopen(url,timeout=10)data = f.read()    # decode the htmlcontentType = f.headers.get(' ...

  3. “几何画板+MathType”双11组合特价,优惠多多

    工欲善其事,必先利其器!几何画板和MathType作为数学老师必备工具,在数学教学中起着非常重要的作用.为回馈老师们做出的伟大贡献,在双11狂欢节期间,MathType和几何画板迎来史上第一次组合特惠 ...

  4. hdu 2686(状压dp)

    题目链接:http://poj.org/problem?id=2686 思路:典型的状压dp题,dp[s][v]表示到达剩下的车票集合为S并且现在在城市v的状态所需要的最小的花费. #include& ...

  5. python3----练习题(爬取电影天堂资源,大学排名,淘宝商品比价)

    import requests import re url = 'http://www.ygdy8.net/html/gndy/dyzz/list_23_{}.html' for n in range ...

  6. Hadoop1.2.1 出现Warning: $HADOOP_HOME is deprecated.的解决方案

    通过启动或停止hadoop我们会发现会出现 “Warning: $HADOOP_HOME is deprecated” 这样一个警告,下面给出解决方案: 不过我们一般推荐第二种,因为我们还是需要$HA ...

  7. OpenSSL学习笔记

    SSL缺省只进行server端的认证,客户端的认证是可选的.以下是其流程图(摘自TLS协议). 加密算法介绍: 对称加密: DES:date encrption standard,56bit 3DES ...

  8. onTouch事件分发

    事件机制 我们知道view中有onTouch,onClick, 1.并且onTouch优先于onClick执行, 2.onTouch有返回值,为true时onClick并不再执行了 因为一切VIew都 ...

  9. Mybatis整理系列(01)————传入参数方式以及#{}与${}的区别

    一.在MyBatis的select.insert.update.delete这些元素中都提到了parameterType这个属性.MyBatis现在可以使用的parameterType有基本数据类型和 ...

  10. ISP (互联网服务提供商)

    ISP(Internet Service Provider),互联网服务提供商,即向广大用户综合提供互联网接入业务.信息业务.和增值业务的电信运营商. ICP(Internet Content Pro ...