List
ArrayList
使用如下代码,我们实例化了一个ArrayList类的对象:
ArrayList<Integer> list=new ArrayL<Integer>
查阅JDK源码就会发现,ArrayList空的构造函数事实上是调用了下面的构造函数:
public ArrayList(intinitialCapacity) {
super();
if(initialCapacity < 0)
thrownewIllegalArgumentException("Illegal Capacity:"+
initialCapacity);
this.elementData = newObject[initialCapacity];
}
public ArrayList( int initialCapacity) {
super();
if(initialCapacity < 0)
thrownewIllegalArgumentException("Illegal Capacity:"+
initialCapacity);
this.elementData =newObject[initialCapacity];
}
其中,最关键的一个对象出现了,就是elementData,它是一个Object数组,
用来存放之后我们将要“add”的对象。
然后来看add方法:
/**
* Appends the specified element to the end of this list.
*
* @parame element to be appended to this list
* @return<tt>true</tt> (as specified by {@link Collection#add})
*/
publicboolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
returntrue;
}
/**
* Appends the specified element to the end of this list.
*
* @parame element to be appended to this list
* @return<tt>true</tt> (as specified by {@link Collection#add})
*/
publicboolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
returntrue;
}
首先,它去调用了一个名字叫做ensureCapacity的方法:
/**
* Increases the capacity of this <tt>ArrayList</tt>instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
publicvoid 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);
}
}
/**
* Increases the capacity of this <tt>ArrayList</tt>instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
publicvoid 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);
}
}
首先来看modCount,它是用来记录当前list对象被改变的次数的;
当第一次调用add方法,elementData的size为0,那么minCapacity
的值为1,一直到第十次调用add,ensureCapacity方法并没有去真正地
增大elementData的容量。直到第十一次调用add方法:modCount记录了
已经十一次修改list,接下来的oldCapacity(10)<minCapacity(11),于是list对象的
“新的容量”(newCapacity)被赋值为16,最后调用Arrays.copyOf对旧数组进行了
一份拷贝。
由于ArrayList内部使用数组实现,所以它长于随机访问,这也仅仅是针对其他集合对象而言的,与数组相比,效率会低很多。
LinkedList
LinkedList部分源码如下:
privatetransient Entry<E> header = new Entry<E>(null, null, null);
privatetransientintsize =0;
/**
* Constructs an empty list.
*/
public LinkedList() {
header.next = header.previous = header;
}
privatetransient Entry<E> header = new Entry<E>(null, null, null);
privatetransientintsize =0;
/**
* Constructs an empty list.
*/
public LinkedList() {
header.next = header.previous = header;
}
它是基于链表实现的,每个entry对象(节点)都包含自己本身的值与对前后两个节点的引用。
我们着重来看下add与remove两个方法。
首先是add:
publicbooleanadd(E e) {
addBefore(e, header);
returntrue;
}
addBefore如下:
private Entry<E> addBefore(Ee, Entry<E> entry) {
Entry<E>newEntry = newEntry<E>(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
双向循环链表就像手拉手那样,每个节点都连接在一起,add,remove开销较小。Remove方法如下:
private E remove(Entry<E> e) {
if (e == header)
thrownewNoSuchElementException();
E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next =e.previous = null;
e.element = null;
size--;
modCount++;
returnresult;
}
而其代价便是短于随机访问。
Map
Map是一种强大的编程工具,它提供了将对象映射到另一个对象的能力(key---->value)。
HashMap
因为map中是key--->value这种键值对,所以它要求key是不可以重复的,value是可以重复的。就像下面的例子:
String[] strs={"a","b","c","c","d","a","e"};
Map<String,Integer> result=new HashMap<String, Integer>();
for(int i=0;i<strs.length;i++){
result.put(strs[i], new Integer(1));
}
System.out.println(result);
上例使用了String作为Map的key,当“a”第二次放进map中时,它被忽略掉了。(此例引申后,我们可以使用它来统计一个字符串数组中,元素的出现次数。)
在能正确的使用map之前,还是要知道为什么它的key是不可以重复的,它是如何保证不能重复的。
查看JDK中HashMap的源码你就会发现:每当将一个对象“put”进map中,JDK都会拿这个对象和map中已经存在的对象进行比较,如下:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
那么两个key是否相同,取决于他们的equal方法和hashCode方法。众所周知,HashSet中存放的元素也是不会重复的,并且它是基于map实现的,
下面的例子重写了equals和hashCode方法:
package com.lemon.map;
import java.util.HashSet;
import java.util.Iterator;
public class HashSetTest2 {
public static void main(String[] args) {
HashSet<StudentModel> set=new HashSet<StudentModel>();
set.add(new StudentModel(new Integer(1),"Bill"));
set.add(new StudentModel(new Integer(1),"Bill"));
set.add(new StudentModel(new Integer(1),"Bill"));
Iterator<StudentModel> it=set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}class StudentModel {
private Integer id;
private String name;
public StudentModel(Integer id,String name) {
this.id=id;
this.name=name;
}
@Override
public String toString() {
return "I am a student which id is["+id.intValue()+"] and name is["+name+"]";
}
@Override
public boolean equals(Object obj) {
return obj instanceof StudentModel&&((StudentModel)obj).id.equals(id)&&((StudentModel)obj).name.equals(name);
}
@Override
public int hashCode() {
int result=17;
int c=new Integer(id).hashCode()+name.hashCode();
result=37*result+c;
return result;
}
}
TreeMap