Java基础-ArrayList源码分析

老规矩也是先画个类图了解一下他的结构:


画了三个类图之后我陷入了沉思,为啥类图和HashSet这么像?翻阅资料,了解集合框架采用了策略模式的设计模式,设计模式等后面再认真整理一份分析一下,这里就不多说了,留着这些问题,后面设计模式版本慢慢揭秘,下面说说几个重要的方法


构造方法:这里了解到如果我们不设置初始值的话默认是10,和hashMap的16有差别,初始化方法就初始化了一个10长度的Object数组

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

boolean add(E e)

 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);
	}
    }

每次添加元素的时候会检查一下容量够不,如果不够,进行扩容,而且他的扩容算法和hashMap不一样,他的计算方法:(oldCapacity * 3)/2 + 1,自己可以算一下,10-->16-->40-->61,看完这里,注意Arrays.copyOf这个方法,这个就是我在String里面强调的很重要的方法,看来万变不离其中,一般集合框架中用到复制的地方肯定会调用这个方法。

int indexOf(Object o)

 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;
    }
这个方法相比String.indexOf(String str)较简单,因为o是一个整体,这样比较就较为简单。

E remove(int index),重点看一下删除方法,这个是按照下标来删除的,看一下他的实现

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; // Let gc do its work

	return oldValue;
    }
看了上面的源码,我做了一个实验,
	public static void testList(){
		List<String> list = new ArrayList<String>();
		for (int i = 0; i < 10000; i++) {
			list.add(i+"asdjflasjdkflasjdlkfjaslkdfj");
		}
		long a = System.currentTimeMillis();
		list.remove(5000);
		long b = System.currentTimeMillis();
		System.out.println(b-a);
	}

我中途换了很多次删除的索引下标,执行结果都是0ms。。。之前我还以为remove需要进行复制,会比较慢,谁知道这么快?不是说数组对插入和删除操作比较耗时吗,于是我加大了长度再来,我加到了长度为100w,删除下标为50w的数据,结果用了1ms,无法理解!!!那我表示以后LinkedList再见。

boolean remove(Object o) 

    public boolean remove(Object o) {
	if (o == null) {
            for (int index = 0; index < size; index++)
		if (elementData[index] == null) {
		    fastRemove(index);
		    return true;
		}
	} else {
	    for (int index = 0; index < size; index++)
		if (o.equals(elementData[index])) {
		    fastRemove(index);
		    return true;
		}
        }
	return false;
    }
这里他会遍历数组,然后调用equals方法比较相等,最终还是通过下标来进行删除操作,我也对他做了性能测试,
	public static void testList(){
		List<String> list = new ArrayList<String>();
		for (int i = 0; i < 1000000; i++) {
			list.add(i+"asdjflasjdkflasjdlkfjaslkdfj");
		}
		String str = "500000asdjflasjdkflasjdlkfjaslkdfj";
		long a = System.currentTimeMillis();
		list.remove(500000);
		long b = System.currentTimeMillis();
		list.remove(str);
		long c = System.currentTimeMillis();
		System.out.println(b-a);
		System.out.println(c-b);
	}
结果是用索引删除花了1ms,用对象删除花了36ms,36ms还是比较久了相对于大并发来说,所以以后删除还是尽量用索引来删除,玩到这里,我又测试了一下map的性能,
	public static void testList() {
		List<String> list = new ArrayList<String>();
		for (int i = 0; i < 1000000; i++) {
			list.add(i + "asdjflasjdkflasjdlkfjaslkdfj");
		}
		String str = "500000asdjflasjdkflasjdlkfjaslkdfj";
		Map<String, Integer> map = new HashMap<String, Integer>();
		for (int i = 0; i < 1000000; i++) {
			map.put(i + "asdjflasjdkflasjdlkfjaslkdfj", 0);
		}
		long a = System.currentTimeMillis();
		list.remove(500000);
		long b = System.currentTimeMillis();
		list.remove(str);
		long c = System.currentTimeMillis();
		map.remove(500000);
		long d = System.currentTimeMillis();
		System.out.println(b - a);
		System.out.println(c - b);
		System.out.println(d - c);

	}

运行结果:


之前看过hashMap的删除流程是通过key得到hash,然后通过hash得到index,再定位到数组的entry,再对这个entry链表做遍历,这么快应该也要归功于hashMap的hash算法。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值