Java容器框架

213 阅读6分钟

类图关系

Collection实现了Iterable接口,通过Iterable接口定义的Interator < T> iterator()方法来获得Iterator对象。

Iterator<T> iter=list.iterator();
if(iter.hasNext()){
    T temp=iter.next();
}//next()方法既返回一个当前Iterator对象,又把游标向下移动一个单位。

1. 列表 List

实现List接口的集合类,集合中的元素插入有序,可以重复。

  1. ArrayList

数组实现,线程不安全,初始大小为10(可传参),扩容×1.5(不可变),查找快、增删慢

扩容、remove、add操作都是拷贝;clear操作是数组元素依次置为null。

Object[] elementData;//核心存储结构
private static final int DEFAULT_CAPACITY = 10;

注意:方法前没有synchronized关键字;

可能出现的异常:java.util.ConcurrentModificationException

解决ArrayList线程不安全方法:

(1)new Vector<>();

(2)Collections.synchronizedList(new ArrayList<>());

(3)new CopyOnWriteArrayList<>(); (√)

写时复制,读写分离的思想:

  1. LinkedList

双向链表实现,线程不安全,查找慢、增删快

Node <E> first/last=new Node<E>(null,null,null);//核心存储结构
  1. Vector

数组实现,线程安全,初始大小10(可传参),扩容×2(可传参),查找快、增删慢

 protected Object[] elementData;//存储结构:数组,同ArrayList

vector初始大小默认为10,capacityIncrement值默认为0,即扩容×2。

synchronized关键字实现线程同步:

2. 集合 Set

实现Set接口的集合类,集合中的元素不可重复。

  1. HashSet

线程不安全无序,可以容纳null元素,内部是HashMap实现的,数据结构是hash表。

HashMap<E,Object> map;//核心存储结构

private static final Object PRESENT=new Object();//value均设为此值

可能出现的异常:java.util.ConcurrentModificationException

解决HashSet线程不安全方法:

(1)Set set=Collections.synchronizedSet(new HashSet<>());

(2)Set set=new CopyOnWriteArraySet<>();

//底层是CopyOnWriteArrayList

  1. LinkedHashSet

线程不安全插入有序,可以容纳null元素,内部是LinkedHashMap实现的,数据结构是hash表+双向链表维护插入顺序

  1. TreeSet

线程不安全元素有序,不能容纳null元素,内部是TreeMap实现的,数据结构是二叉树

-HashSet和LinkedHashSet判定元素重复的原则:

判定两个元素的hashCode返回值是否相同,不同则返回false;若两者hashCode相同,则判定equals方法,不同返回false,相同返回true。hashcode->equals()

-Treeset判定元素重复的原则:

元素需要继承Comparable接口,比较两个元素的compareTo方法

TreeMap <E,Object>;//核心存储结构

3. 映射 Map

  1. HashMap

K-V对,K和V都允许为null,线程不安全无序,数据结构是hash表。

Java(HashMap)中规定,两个内容相同(equals()为true)的对象必须具有相等的hashCode,否则整个存储过程就发生了悖论。

transient Node<K,V>[] table;//存储结构,Node对象数组
static class Node<K,V> implements Map.Entry<K,V>//Node内部类实现了Entry接口

HashMap的位桶数组,初始大小为16(可以在取模和扩容时作优化,可以修改),其中元素达到0.75(负载因子,可以修改)×length,就重新调整数组大小变为原来的2倍。扩容很耗时,本质是定义新的更大的数组,并将旧数组内容挨个拷贝到新数组中。HashMap最大容量为1<<30即2^30(int4个字节,含符号位)。

JDK7中,HashMap底层是数组+链表实现;JDK8中,HashMap在存储一个元素时,当对应链表长度大于8时,链表就转换为红黑树,这样又大大提高了查找的效率,在长度小于6时,转回链表。

JDK7中扩容时由于线程不同步数组的链表可能出现环,就可能出现死锁问题;JDK8中改进扩容机制,使用尾插法,使扩容后的hashMap保持顺序,出现死锁的概率大大降低。

位桶数组大小是2的次幂,JDK1.8扩容时只需要判断Hash值的新增参与运算的位是0还是1就直接迅速计算出了扩容后的储存方式,而不需要重新进行异或。

//JDK1.8
putVal方法{
	如果table为空或长度为0,则进行resize()扩容;
	计算要插入元素的hash值,即对应位桶数组下标((n-1)&hash),如果对应位置为空{
		即此时无hash冲突,直接放入,作为对应位置第一个Node对象;
	}
	发生hash冲突{
		如果对应位置第一个Node对象key值与要put对象的key值相等,直接覆盖;
		否则判断是否为TreeNode数据结构,是则直接调用putTreeVal方法;
		不是TreeNode结构,即为链表结构{
			遍历链表,如果遍历到key相等的Node对象,则覆盖;
            如果遍历到尾部也没有key相等的Node对象{
				则创建一个新的Node对象,插入尾部;
				如果binCount链表长度大于8,则转为红黑树结构存储;
			}
		}
	}
	如果位桶数组长度超过阈值,就resize()扩容;
}
//JDK1.8
getNode方法{
	如果位桶数组table不为空&&长度不为0&&要找的key的hash值在table中对应位置第一个元素不为null{
    	如果对应位置第一个元素就是要找的值,直接返回;
    	否则,如果对应位置第一个元素的next不为null{
    		如果是红黑树数据结构,调用getTreeNode直接返回;
            否则为链表结构,遍历链表,找到则返回;
   		}
    }
	执行到此处也说明没找到,返回null;
}

HashMap可能出现的异常:java.util.ConcurrentModificationException

解决HashMap线程不安全方法:

(1)Collections.synchronizedMap(new HashMap<>());

(2)new ConcurrentHashMap<>();

  1. LinkedHashMap

基于双向链表维持插入顺序的HashMap

  1. TreeMap

V允许为null,基于红黑树的Map,可以根据key的自然排序或者compareTo方法进行key元素的排序输出。需要实现Comparable接口。

TreeMap和HashMap实现了同样的接口Map,因此用法对于调用者来说没有区别。HashMap效率高于TreeMap;在需要排序的Map时才选用TreeMap,对应类要实现Comparable接口。

  1. Hashtable

K-V对,K和V都不允许为null,线程安全,无序。初始化大小为11(相对来说素数导致的冲突概率小于合数),扩容*2+1。

HashTable使用synchronized锁住整张Hash表实现线程安全。

  1. Properties

继承于Hashtable,可以将K-V对保存在文件中

4. 工具类

  1. Arrays

作用对象是数组,常用方法:

    sort(排序)
    binarySearch(二分查找)
    copyOf(批量复制)
    fill(批量赋值)
    equals(等价性比较)

2. Collections

作用对象是Collection及其子类,常用方法:

    sort
    binarySearch
    fill
    maxmin
    reverse(反序)

补充

对象比较(如使用Collections.sort(Target))时:

  1. 需要对象实现Comparable接口并实现compareTo方法。
  2. 或者新建一个实现Comparator接口的类对象并实现compare方法作为参数传递(适用于对象类不可更改的情况)。