1. 数组和集合
数组只能存储基本数据类型,并且长度固定,不适合存储数量未知的.
集合可以存储对象,容量可以扩展.
2. Collection结构
集合接口方法:
public interface Collection<E> extends Iterable<E> {
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
}
2.1 Set
Set特点就是无序,不能重复,就是简单的把数据放到集合当中去.
以HashSet来看,从其中的构造函数可以看到,还是创建了一个HashMap对象来存储的
public HashSet() {
map = new HashMap<E,Object>();
}
在调用Set实现类的add()方法的时候,会调用HashMap的put()方法
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;
}
从上述put的代码中可以看到,首先获取key值(键值)对应的hash值,算法如下:
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
如果哈希值一致,则不放入hashcode录入,如果一致,然后通过调用equals()方法,如果一致,则同样不放入hashcode录入,如果不一致,则放入set中.
Hash哈希算法的意义在于提供了一种快速存取数据的方法,它用一种算法建立键值与真实值之间的对应关系。散列表又称为哈希表。散列表算法的基本思想是:以结点的关键字为自变量,通过一定的函数关系(散列函数)计算出对应的函数值,以这个值作为该结点存储在散列表中地址。
当散列表中的元素存放太满,就必须进行再散列,将产生一个新的散列表,所有元素存放到新的散列表中,原先的散列表将被删除。在Java语言中,通过负载因子(load factor)来决定何时对散列表进行再散列。例如:如果负载因子0.75,当散列表中已经有75%位置已经放满,那么将进行再散列。
负载因子越高(越接近1.0),内存的使用效率越高,元素的寻找时间越长。负载因子越低(越接近0.0),元素的寻找时间越短,内存浪费越多.
LinkedHashSet有HashSet的所有特性,这个是保证put数据的有一定插入的次序.
TreeSet是有序的,TreeSet实现了SortedSet接口,TreeSet中元素将按照升序排列,缺省是按照自然顺序进行排列,意味着TreeSet中元素要实现Comparable接口,我们可以在构造TreeSet对象时,传递实现了Comparator接口的比较器对象.
总之,HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet.
2.2 List
List里存放的对象是有序的,同时也是可以重复的,List关注的是索引,拥有一系列和索引相关的方法,查询速度快。因为往list集合里插入或删除数据时,会伴随着后面数据的移动,所有插入删除数据速度慢.
ArrayList是有序的,并且可以存储相同的数据,列出list的add()方法
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private transient Object[] elementData;
集合List中底层还是使用数组的形式,只不过数组的长度是可变的,可以扩容的.
LinkedList是采用链表的结构,每个元素存放着前一个元素和后一个元素的指针地址.
ArrayList查询速度快,尾部插入和删除也比较快,一般的删除和插入比较慢
LinkedList删除和插入比较快,但是查询比较慢.
2.3. Map
map并没有实现Collection接口,但是它也属于集合类中的元素,map的键是唯一的,值是可以重复的.
实现类:HashMap、Hashtable、LinkedHashMap和TreeMap
2.3.1 HashMap
HashMap是最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。因为键对象不可以重复,所以HashMap最多只允许一条记录的键为Null,允许多条记录的值为Null,是非同步的
2.3.2 Hashtable
Hashtable与HashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率较低。
2.3.3 ConcurrentHashMap
线程安全,并且锁分离。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。
2.3.4 LinkedHashMap
LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历的时候会比HashMap慢,有HashMap的全部特性。
2.3.5 TreeMap
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。不允许key值为空,非同步的;