JAVA总结--集合

1、集合树状图

Collection:最基本的集合接口

  ----List:有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问

    ----ArrayList:异步

    ----LinkedList:实现了List和Queue的双接口

    ----Vector:同步

      ----Stack:后进先出,同步

  ----Queue:先入先出(FIFO)数据结构的集合

    ----LinkedList:实现了List和Queue的双接口

  ----Set:无序集合,集合中的元素不可以重复,最多有一个null元素

    ----EnumSet:枚举类型专用,单一枚举类型,无null元素,异步

    ----SortedSet:继承了Set接口,并添加了comparator()自定义排序方法等方法,可以重写该方法进行客户化排序

      ----TreeSet:实现了SortedSet接口(其中JDK类库中有些类如String,Float,Integer,Double等已经实现了Comparable接口作为TreeSet的自然排序方式)。(Tips:Set内部源码仍以Map为基础,是value为空对象的Key集合)

    ----HashSet:异步,最多有一个null元素

      ----LinkedHashSet:继承HashSet,调用了 LinkedHashMap中记录插入元素顺序的recordAccess()方法。

 

MAP:保存Key-value对形式的元素,访问时只能根据每项元素的key来访问其value

  ----HashMap:异步,允许null,即null value和null key

    ----WeakHashMap:对key实行“弱引用”,如果一个key不再被外部所引用,该key可以被GC回收

  ----Hashtable:同步,不允许null

  ----SortedMap:继承了Map接口,并添加了comparator()自定义排序方法等方法,可以重写该方法进行客户化排序

      ----TreeMap:实现了SortedMap接口(其中JDK类库中有些类如String,Float,Integer,Double等已经实现了Comparable接口作为TreeMap的自然排序方式)

 

2、集合统计信息

对于Set、List和Map三种集合,最常用的实现类分别是HashSet、ArrayList和HashMap三个实现类;

Vector、HashTable、Properties和Stack是同步类,所以它们是线程安全的,可以在多线程环境下使用;

ArrayList、HashMap、TreeMap和HashTable类提供对元素的随机访问;

 

3、集合遍历

Iterator(迭代器) :hasNext()  next()  remove()

当前遍历的集合元素被更改的时候,会抛出ConcurrentModificationException,避免报错CopyOnWriteArrayList;(Enumeration不会,不安全)

for(for循环):

一:for(Iterator iterator = list.iterator();iterator.hasNext();)

二:Iterator iterator = list.iterator();   while(iterator.hasNext()){ 

三:for (Object object : list) {(从JDK1.5开始出现的语法,相当于 while(iterator.hasNext(),所以这里的for循环内部还是迭代器方式遍历)

四: for (int i = 0 ;i<list.size();i++) {  

 

4、原理

一、Hashtable、HashMap、HashSet的实现原理:

底层数据结构是哈希表;

首先判断hashCode()值是否相同

是:继续执行equals(),看其返回值

  是true:说明元素重复,不添加

  是false:就直接添加到集合

否:就直接添加到集合

最终:自动生成hashCode()和equals()即可;

 

 

二、线程安全:Hashtable和ConcurrentHashMap

Hashtable:synchronized是针对整张Hash表的,即每次锁住整张表让线程独占;

ConcurrentHashMap:锁分离,使用了多个锁来控制对hash表的不同部分(段Segment);但size()和containsValue()等方法依然是跨段对整个表进行加锁(按顺序锁定所有段);ConcurrentHashMap的迭代器为弱一致性,即在遍历并遇到修改时会复制数据进行遍历,遍历结束再将迭代器指针指向新数据。

 

 

 

各种集合的原理详解---jdk源码

Arraylist    LinkList  HashMap  HashSet

1.ArrayList

1  private transient Object[] elementData;
2  private int size;
 1 public ArrayList(int paramInt)
 2   {
 3     if (paramInt < 0)
 4       throw new IllegalArgumentException("Illegal Capacity: " +      paramInt);
 6        this.elementData = new Object[paramInt];
 7   }
 8 
 9   public ArrayList()
10   {
11       this(10);
12   }

ArrayList构造方法,支持预定长度,使用类中的全局变量Object类型的数组,进行数据的保存。

 

2.LinkList  

1 private transient Entry<E> header;
2 private transient int size;
 1   public LinkedList()
 2   {
 3     this.header = new Entry(null, null, null);
 4     this.size = 0;
 5 
 6     this.header.next = (this.header.previous = this.header);
 7   }
 8 
 9   public LinkedList(Collection<? extends E> paramCollection)
10   {
11     addAll(paramCollection);
12   }

LinkList  构造方法,支持预设值一个集合,方法addAll为for循环创建并增加集合数据。

 1 private static class Entry<E>
 2   {
 3     E element;
 4     Entry<E> next;
 5     Entry<E> previous;
 6 
 7     Entry(E paramE, Entry<E> paramEntry1, Entry<E> paramEntry2)
 8     {
 9       this.element = paramE;
10       this.next = paramEntry1;
11       this.previous = paramEntry2;
12     }
13   }

LinkList  中的Entry 为静态内部私有类,包括前后指针和数据。

 

3.HashMap 存储/读取数据原理:

//定义
//private transient Set<Map.Entry<K, V>> entrySet;


public HashMap(int paramInt) { this(paramInt, 0.75F); } public HashMap() { this.entrySet = null; this.loadFactor = 0.75F; this.threshold = 12; this.table = new Entry[16]; init(); } public HashMap(Map<? extends K, ? extends V> paramMap) { this(Math.max((int)(paramMap.size() / 0.75F) + 1, 16), 0.75F); putAllForCreate(paramMap); }

HashMap在无参数下,初始化entry为16(2的指数),仅保存12个元素;以16为基础翻倍扩容,存储数据以12为基础翻倍增加;

 1 public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable {
 2     private static final int MINIMUM_CAPACITY = 4;
 3 ...
 4     transient HashMapEntry<K, V>[] table;
 5 ...
 6     private static final Entry[] EMPTY_TABLE
 7             = new HashMapEntry[MINIMUM_CAPACITY >>> 1];
 8   ...       
 9    @Override public V put(K key, V value) {
10         if (key == null) {
11             return putValueForNullKey(value);
12         }
13 
14         int hash = Collections.secondaryHash(key);
15         HashMapEntry<K, V>[] tab = table;
16         int index = hash & (tab.length - 1);
17         for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
18             if (e.hash == hash && key.equals(e.key)) {
19                 preModify(e);
20                 V oldValue = e.value;
21                 e.value = value;
22                 return oldValue;
23             }
24         }
25 
26         // No entry for (non-null) key is present; create one
27         modCount++;
28         if (size++ > threshold) {
29             tab = doubleCapacity();
30             index = hash & (tab.length - 1);
31         }
32         addNewEntry(key, value, hash, index);
33         return null;
34     }
35     ...
36         public V get(Object key) {
37         if (key == null) {
38             HashMapEntry<K, V> e = entryForNullKey;
39             return e == null ? null : e.value;
40         }
41 
42         int hash = Collections.secondaryHash(key);
43         HashMapEntry<K, V>[] tab = table;
44         for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
45                 e != null; e = e.next) {
46             K eKey = e.key;
47             if (eKey == key || (e.hash == hash && key.equals(eKey))) {
48                 return e.value;
49             }
50         }
51         return null;
52         }
53     ...
54     }

 分析下put方法的实现:

  •     if (key == null) {

                 return putValueForNullKey(value);

            }

        首先判断是否为null,如果为null则特殊处理;

  •     2、int hash = Collections.secondaryHash(key);

        获取Key的二级hash值,其中Collections.secondaryHash方法的实现就是把Key的hashcode值做一定改变;

 

  •     int index = hash & (tab.length - 1);

     通过刚才计算的hash值来获取该key应该存放在数组的下标位置,也就是获取该数据应该存储在table数组的哪个位置;

 

  •     for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {

                       if (e.hash == hash && key.equals(e.key)) {

                                  preModify(e);

                                  V oldValue = e.value;

                                 e.value = value;

                               return oldValue;

            }

       }

    如果已经有该key存在了,则覆盖这个key的值value。

    注意这里的判断:因为只有两个对象的hashcode值相等并且两个对象用equals判断返回true时,才去覆盖原有的值;

  •   if (size++ > threshold) {

                      tab = doubleCapacity();

                      index = hash & (tab.length - 1);

          }

        addNewEntry(key, value, hash, index);

        如果该key不存在,或者发生碰撞的对象不是一个对象时,则需要把它存储下来。首先如果存储数量已经大于数组大小,则把数组双倍扩大。然后再把键值对保存到数组中。

        注意这里保存的时候,如果数组存储位置原本就存在键值对,那么则把新的键值对对象保存到旧的键值对 对象next变量中,构成链表。

 

  • hashMap是table和entry的组合,他的扩容机制,保证table与entry比例均衡,对table的查询与entry的修改的优点进行均衡采用。

4.HashSet

  private transient HashMap<E, Object> map;
  private static final Object PRESENT = new Object();
public HashSet()
  {
    this.map = new HashMap();
  }
  public boolean add(E paramE)
  {
    return (this.map.put(paramE, PRESENT) == null);
  }

由源码可见,HashSet用的就是HashMap实现的,其类中定义一个final状态的Object对象,作为每个数据元素的value,HashSet仅仅使用HashMap的key进行数据的保存,其remove等方法均调用HashMap的方法实现。

 

posted on 2017-09-12 13:37  奇天异下  阅读(372)  评论(0编辑  收藏  举报

导航