【Java基础】集合

0.

Java集合类库将接口和实现分离。


Java集合类库的基本接口是Collection (java.util

public interface Collection<E> extends Iterable<E>

迭代器Iterator

public interface Iterator<E>  (java.util)

Methods  
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).
传统(如C++)的迭代器是基于数组索引的,因此得到一个迭代器之后,可以进行i++操作来移动迭代器。

但是,Java迭代器并不是这样运作的,查找操作与位置的变更是联系在一起的。查找一个元素的唯一操作就是next(),而执行该查找的同时会使迭代器的位置向前移动

  • 迭代器模型:

应该将Java迭代器看成是位于各个元素之间的:当调用next()的时候,迭代器便越过下一个元素,并且返回它刚刚越过的那个元素的引用;

【Java基础】集合_第1张图片

  • 迭代器的遍历:

方法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集合类库框架(部分)

【Java基础】集合_第2张图片

集合类库 - 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封装了一个 动态再分配的对象数组;查找和修改速度较快,缺点是插入和删除速度较慢。

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继承自Vector,同样使用数组保存数据。

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的实现机制

【Java基础】集合_第3张图片

/**
     * 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的对象必须实现hashCodeequals方法;

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.





你可能感兴趣的:(java)