突破程序员基本功的16课-3(Map,List)

Map和List

Map的key是一个Set,可是Map的value不是一个List,不过能够将其看作一个List。java

Map的values()方法返回的是map的全部value,可是它不是一个List,而是一个内部类Values数组

HashMap中的特性安全

查看values源代码:性能

public Collection<V> values() {
        Collection<V> vs = values;
        return (vs != null ? vs : (values = new Values()));
    }

能够看出,values方法返回的是一个集合:newValues()~!this

该内部类以下:spa

private final class Values extends AbstractCollection<V> {
        public Iterator<V> iterator() {
//返回newValueIterator方法的返回值
            return newValueIterator();
        }
        public int size() {
//返回外部类实例的size实例变量的返回值
            return size;
        }
        public boolean contains(Object o) {
//返回外部类实例的containsValue(o)的返回值
            return containsValue(o);
        }
        public void clear() {
//调用外部类实例的clear方法
            HashMap.this.clear();
        }
}

这个Values类继承AbstractCollection,可是它却不算一个集合类,由于它没有add方法,即不能够添加元素,不能存储任何java对象。它的做用是遍历Map中全部的value线程

遍历方法iterator():主要是依赖ValueIterator类:code

查看newValueIterator类:对象

查看nextEntry方法:继承

在TreeMap中方法是相似的,可是因为它是经过红黑树实现的,因此它遍历的时候仍是利用的二叉树的特色:查找子节点。。。。再也不叙述

综上:

Map的values方法返回的是map的全部value,该value是一个不能存储元素的Collection的实现类,因此程序遍历该集合便是遍历map的全部value

Map和List的实现没有太大的类似之处,就是用法挺像

 

ArrayList和LinkedList

List主要有三个实现类:ArrayList,Vector,LinkedList

Vector

Vector有一个实现类:Stack,它只比Vector多了5个方法,本质上仍是一个Vector,它们是线程安全的,不过已经再也不推荐使用了,从1.6版本开始,已经被Deque取代,它也有一个实现类ArrayDeque,它既有队列的性质又有栈的性质

Vector基本已被ArrayList取代,它相比ArrayList,优点是线程安全的,不过,使用ArrayList时,也可使用Collection的静态方法来将其包装成线程安全的,故Vector再也不叙述

在ArrayList中有一行

private transient Object[] elementData;

它是用来保存集合的元素的数组,经过transient修饰,保证了序列化时不会直接序列化该数组,而是经过ArrayList提供的writeObject和readObject方法来实现定制序列化。

ArrayList和LinkedList性能差别

ArrayList底层采用数组存储,在数据插入的时候,须要作两件事:

1:保证ArrayList底册封装的数组长度大于集合元素的个数

2:将插入位置以后的全部元素总体向后移动一格

查看源代码:

public E remove(int index) {
		//若是index大于等于size,抛出异常
		RangeCheck(index);
		modCount++;
		//保证index处的元素
		E oldValue = (E) elementData[index];
		//计算总体搬家的元素个数
		int numMoved = size - index - 1;
		if (numMoved > 0)
			System.arraycopy(elementData, index + 1, elementData, index,
					numMoved);
		//释放被删除的元素,让GC回收
		elementData[--size] = null; // Let gc do its work

		return oldValue;
	}

能够看出,当向ArrayList添加删除元素的时候,都须要对数组进行总体搬家,性能较差,可是查找的时候:

直接经过数组取值,性能和数组几乎同样好

添加时:

public void add(int index, E element) {
	if (index > size || index < 0)
	    throw new IndexOutOfBoundsException(
		"Index: "+index+", Size: "+size);
//保证ArrayList的底层数组老是能够保存全部元素
	ensureCapacity(size+1);  // Increments modCount!!
/*将elementData数组中index后面的全部元素向后移动一格,这样就能够将index处的位置空出来*/
	System.arraycopy(elementData, index, elementData, index + 1,
			 size - index);
//将新元素放置在index处
	elementData[index] = element;
	size++;
    }

这里调用了ensureCapacity,源码以下:

能够看到ArrayList在超过当前容量时,固定的将新容量扩充至原来的1.5倍

LinkedList本质上是一个双向链表,所以它也是使用内部类Entry来保存数据元素:

private static class Entry<E> {
	//集合的元素
	E element;
//指向下一链表节点的引用
	Entry<E> next; 
//指向上一链表节点的引用
	Entry<E> previous;
//构造器
	Entry(E element, Entry<E> next, Entry<E> previous) {
	    this.element = element;
	    this.next = next;
	    this.previous = previous;
	}
}

能够看出Entry表明双向链表的一个节点,element是元素自己的值,另外就是链表节点的上/下节点的引用,插入数据时形如:

源码:

主要用到两个方法:

entry(index)和addBefore();

emrty(index)是get(int index)的底层实现,因为底层是链表,因此LinkedList必须一个元素一个元素的遍历,直到找到第index个元素

由此能够看出每次查找元素都须要遍历链表自己,性能较差,不能像ArrayList同样直接拿到index处的元素

同时看看get(int index)的源码,就是对entry方法的简单封装

public E get(int index){
    return entry(index).element;
}

再看看单纯的插入源码:

若是只是单纯的插入,LinkedList的性能会很到,可是若是在指定位置插入的话,性能也会受影响,由于它涉及到了查找元素,增长了搜索过程。

删除也是同理:

先查找,再删除

具体实现:

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;
//将被删除的两个元素的引用都置为空,gc回收
        e.next = e.previous = null;
        e.element = null;
	size--;
	modCount++;
        return result;
    }

能够看出首先将被删除元素保存起来,再将被删除元素的next赋给它上一节点的next,将其previous赋给它next节点的previous,即将它本身自己从链表中剔除了,再把它自己置为null,最后返回以前保存的被删除元素

整体来看,ArrayList的性能优于LinkedList,除非常常添加元素时,否则优先考虑ArrayList

迭代器

Iterator是一个迭代器接口,用于统一的迭代各类Collention集合,至于各集合返回的Iterator实现类,程序不关心,迭代的方式由各集合的实现类本身实现。

使用Iterator迭代是不要删除集合中的元素,否则会发生ConcurrentModeficationException

相关文章
相关标签/搜索