0.
Java集合类库将接口和实现分离。
Java集合类库的基本接口是Collection (java.util)
public interface Collection<E> extends Iterable<E>
迭代器Iterator
public interface Iterator<E> (java.util)
Modifier and Type | Method and Description |
---|---|
boolean | hasNext()
Returns
true if the iteration has more elements.
|
E | next()
Returns the next element in the iteration.
|
void | remove()
Removes from the underlying collection the last element returned by this iterator (optional operation).
|
但是,Java迭代器并不是这样运作的,查找操作与位置的变更是联系在一起的。查找一个元素的唯一操作就是next(),而执行该查找的同时会使迭代器的位置向前移动。
- 迭代器模型:
应该将Java迭代器看成是位于各个元素之间的:当调用next()的时候,迭代器便越过下一个元素,并且返回它刚刚越过的那个元素的引用;
- 迭代器的遍历:
方法1:调用next之前应该先执行hasNext()检测,防止next()抛出NoSuchElementException
Collection<String> c = ...;
Iterator<String> iter = c.iterator();
while(iter.hasNext()){
String element = iter.next();
... ...
}
方法2:for each
for (String element : c){
... ...
}
- 移除元素:
Iterator接口的remove方法会移除上次调用next()方法返回的元素。
i.e. 移除第一个位置上的元素:
Iterator<String> iter = c.iterator();
iter.next(); // skip over the first element
iter.remove(); // now, remove it
如果调用remove()方法之前没有调用next(),或者在上一次调用next()方法之后已经调用过一次remove(),则此时调用remove()会抛出IllegalStateException;
Java集合类库框架(部分)
集合类库 - List接口
List将元素维护在特定的序列中,并且允许重复的值。
List接口提供了名为ListIterator的迭代器。
List在数据结构中表现为数组、向量、链表、堆栈、队列等。
LinkedList
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable (java.util)
LinkedList的特点
LinkedList是基于链表的集合(java中的链表都是 循环双重连接的,forward/backward node):public LinkedList() {
header.next = header.previous = header;
}
LinkedList的实现机制
LinkedList的使用
ArrayList
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable (java.util)
ArrayList的特点
ArrayList的实现机制
从JDK源码中可以看出,其带有两个属性:
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private transient Object[] elementData;
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
当存储空间不足时,重新分配空间:
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);
}
}
ArrayList的使用
Vector
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable (java.util)
Vector的特点
/**
* The array buffer into which the components of the vector are
* stored. The capacity of the vector is the length of this array buffer,
* and is at least large enough to contain all the vector's elements.
*
* <p>Any array elements following the last element in the Vector are null.
*
* @serial
*/
protected Object[] elementData;
/**
* The number of valid components in this {@code Vector} object.
* Components {@code elementData[0]} through
* {@code elementData[elementCount-1]} are the actual items.
*
* @serial
*/
protected int elementCount;
Vector的实现机制
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);
}
}
Vector中大部分方法都使用 synchronized修饰,是线程安全的。
Vector的使用
ArrayList V.S. Vector
- Vector的所有方法都是同步的;ArrayList的方法不是同步的;
- 在不需要同步时,建议使用ArrayList;
Stack
public class Stack<E> extends Vector<E> (java.util)
Stack的特点
Stack的实现机制
Stack的使用
集合类库 - Set接口
public interface Set<E> extends Collection<E> (java.util)
Set集合和List集合都是存放的单个元素的序列,但是Set集合不允许有重复元素。
在Set接口中没有新增任何方法,所有方法均来自其父接口。它无法提供像List中按位存取的方法。在数学上一个集合有三个性质:确定性、互异性、无序性。
HashSet
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable (java.util)
HashSet的特点
HashSet中存放的元素时无序的,底层是用HashMap实现的:
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
其中Key时要放入的元素,value是一个Object类型的名为PRESENT的敞亮。由于使用了散列函数,因此其存取速度是非常快的。
HashSet的实现机制
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<E,Object>();
}
/**
* Constructs a new set containing the elements in the specified
* collection. The <tt>HashMap</tt> is created with default load factor
* (0.75) and an initial capacity sufficient to contain the elements in
* the specified collection.
*
* @param c the collection whose elements are to be placed into this set
* @throws NullPointerException if the specified collection is null
*/
public HashSet(Collection<? extends E> c) {
map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
在HashSet中有一个负载因子Load Factor。在HashSet的默认实现中,初始容量为16,负载因子为0.75,也就是说当有75%的空间以被使用,将会在进行一次再散列,之前的散列表(数组)将被删除,新增加的散列表是之前散列表长度的2倍,最大值为Integer.MAX_VALUE。
- 负载因子越高,内存使用率越大,元素的寻找时间越长;
- 负载因子越低,内存使用率越小,元素的寻找时间越短;
当哈希值相同时,将存放在同一个位置,使用链表方式依次链接下去。
HashMap中的resize方法如下(HashSet是基于HashMap实现的):
/**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
transient Entry[] table;
/**
* Rehashes the contents of this map into a new array with a
* larger capacity. This method is called automatically when the
* number of keys in this map reaches its threshold.
*
* If current capacity is MAXIMUM_CAPACITY, this method does not
* resize the map, but sets threshold to Integer.MAX_VALUE.
* This has the effect of preventing future calls.
*
* @param newCapacity the new capacity, MUST be a power of two;
* must be greater than current capacity unless current
* capacity is MAXIMUM_CAPACITY (in which case value
* is irrelevant).
*/
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}
/**
* Transfers all entries from current table to newTable.
*/
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
do {
Entry<K,V> next = e.next;
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
HashSet的使用
HashSet是允许放空值的。
LinkedHashSet
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, Serializable (java.util)
LinkedHashSet的特点
LinkedHashSet保证了按照插入顺序有序。
LinkedHashSet的实现机制
LinkedHashSet使用HashSet的构造函数:
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
}
可以看出LinkedHashSet底层是使用LinkedHashMap实现的。
LinkedHashSet继承自HashSet,并没有提供额外的供使用的方法,所以在使用时与HashSet基本相同。
LinkedHashSet的使用
TreeSet
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable (java.util)
TreeSet的特点
TreeSet中所放的元素是有序的,并且元素是不能重复的。
基于红黑树。
TreeSet的实现机制
TreeSet底层使用TreeMap使用,和HashSet一样,将每个要放入的元素放到key的位置,value位置放的是一个Object类型的常量。
TreeSet的使用
集合类库 - Map接口
public interface Map<K,V>
Map中的每个成员方法由一个关键字key和一个值value构成。Map接口不直接继承自Collection接口,因为它包装的是一组成对的“键-值”对象的集合,而且 在Map接口的集合中也不能有重复的key出现,因为每个键只能与一个成员元素相对应。
HashMap
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
- HashMap是未同步的;
- 允许null key 和 null value;
- 方法public V get(Object key)返回null时,即表示hashmap中没有该key,也表示该key所对应的value是null;因此使用containsKey来判断是否存在某个key;
- HashMap利用key的hashCode重新计算hash值;
HashMap的特点
HashMap的实现机制
HashMap基于hash数组实现,若key的hash值相同则使用链表方式进行保存,可参见HashSet中的说明。
/**
* The table, resized as necessary. Length MUST Always be a power of two.
*/
transient Entry[] table;
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
/**
* Creates new entry.
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
}
Entry是一个节点,它持有下一个元素的引用,这样就构成了一个链表。
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
这个方法根据hashCode记当前table的长度得到该元素应该存放的位置,即在table中的索引。
当元素超过threshold时,再哈希的过程参看HashSet。
HashMap的使用
HashMap的存取速度快。
HashMap的遍历:
public static void useWhileSentence(Map<Integer, String> m) {
Set s = (Set<Integer>)m.keySet();
Iterator<Integer> it = s.iterator();
int Key;
String value;
while(it.hasNext()) {
Key = it.next();
value = (String)m.get(Key);
System.out.println(Key+":\t"+value);
}
}
public static void useWhileSentence2(Map m) {
Set s = m.entrySet();
Iterator<Map.Entry<Integer, String>> it = s.iterator();
Map.Entry<Integer, String> entry;
int Key;
String value;
while(it.hasNext()) {
entry = it.next();
Key = entry.getKey();
value = entry.getValue();
System.out.println(Key+":\t"+value);
}
}
public static void useForSentence(Map<Integer, String> m) {
int Key;
String value;
for(Map.Entry<Integer, String> entry : m.entrySet()) {
Key = entry.getKey();
value = entry.getValue();
System.out.println(Key+":\t"+value);
}
}
HashTable
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable (java.util)
- HashTable是同步的;
- key或value均不能为null object;
- HashTable直接使用做为key对象的hashCode方法,因此作为key的对象必须实现hashCode和equals方法;
HashTable的特点
HashTable实现Map接口,继承自Dictionary类。任何非空的(key - value)均可以放入其中。
HashTable的实现机制
HashTable的部分方法使用synchronized保证线程安全。
HashTable的使用
HashTable V.S. HashMap
- 如果不考虑线程安全,建议使用HashMap代替HashTable;如果需要线程安全,建议使用ConcurrentHashMap代替HashTable;
LinkedHashMap
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
LinkedHashMap的特点
LinkedHashMap继承自HashMap实现了Map接口。和HashMap一样,LinkedHashMap允许key和value均为null。
LinkedHashMap的实现机制
LinkedHashMap与HashMap的不同之处在于:LinkedHashMap维护着运行于所有条目的双重连接列表,此链接列表可以时插入顺序或者访问顺序。
LinkedHashMap的使用
TreeMap
参见TreeSet
集合类比较
interface | 集合类 | 插入节点 | 删除节点 | 查找节点 | 遍历 | 存储方式 | 线程安全 | 其他 |
---|---|---|---|---|---|---|---|---|
List | ||||||||
Vector | 慢 | 慢 | 快 | 使用迭代器 | 数组 | 是 | ||
ArrayList | 慢 | 慢 | 快 | 使用迭代器 | 数组 | 否 | ||
LinkedList | 快 | 快 | 慢 | 使用迭代器 | 链表 | 否 | ||
Set | ||||||||
TreeSet | 使用迭代器 | 红黑树 | 否 | 继承TreeMap | ||||
HashSet | 使用迭代器 | 散列表 | 否 | 继承HashMap | ||||
LinkedHashSet | 使用迭代器 | 散列表 | 否 | 继承LinkedHashMap | ||||
Map | ||||||||
TreeMap | key set, EntrySet | 红黑树 | 否 | |||||
HashMap | key set, EntrySet | 散列表 | 否 | |||||
LinkedHashMap | key set, EntrySet | 散列表 | 否 | |||||
HashTable | key set, EntrySet | 散列表 | 是 |
集合类库Utils
Collections(集合类包装器)
(java.util)
public class Collections extends Object
包含一系列的静态方法,操作或者返回一个集合类;
Arrays
(java.util)
public class Arrays extends Object
包含操纵数组的静态方法;
算法
常用集合操作
删除集合类中的特定元素
i.e. 删除ArrayList中特定的元素:
方法1:
for (int i = 0, len = list.size(); i < len; i++){
if (list.get(i) == XXX){
list.remove(i);
--len;
--i;
}
}
方法2:
Iterator<String> sListIterator = list.iterator();
while(sListIterator.hasNext()){
String e = sListIterator.next();
if (e.equals("XXX")){
sListIterator.remove();
}
}
推荐使用方法2.