3.java中的数据结构

喜夏-厌秋 提交于 2020-02-04 18:09:12

java中关于数据结构的工具类,暂时就这么称呼吧,其实也贼拉多,害看图包

上面的图肯定时不全,知识一些常用的。简单看一下上面列举出来的“工具类”的源码和一些常见的面试问题:

List接口中的“工具类”,是一个有序集合,可以重复。

ArrayList:底层使用数组实现的,数据是有序数据(插入的顺序),具有索引查找比较快,线程不安全。

属性:
  //默认容量
  private static final int DEFAULT_CAPACITY = 10;
  //空集合
  private static final Object[] EMPTY_ELEMENTDATA = {};
  //默认空集合
  private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  //数据存储数组,不参与序列化
  transient Object[] elementData;
  //容量
  private int size;
  //最大容量
  private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  构造方法:
  /*
  *指定长度的构造方法
  */
   public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    /*
    *空的构造方法
    */
     public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    /*
    *将其他Collection转化为ArrayList的构造方法
    */
     public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            this.elementData = EMPTY_ELEMENTDATA;
        }
    } 

增删改查的方法:
    /*
    *查找方法
    */
    public E get(int index) {
        //检查index是否非法
        rangeCheck(index);
        return elementData(index);
    }
    /*
    *更新方法
    */
     public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
    //如果index非法抛出异常
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    //获取指定index的元素
     @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }
    /*
    *添加方法
    */
     public boolean add(E e) {
         //检查容量是否够
        ensureCapacityInternal(size + 1);  
        elementData[size++] = e;
        return true;
    }
    /*
    *下面这些是首先检查是不是空数组添加元素,如果空数组添加元素,那么初始这个数组长度为默认值
    *如果并不是空数组,先检查扩容后会不会超过最大容量,如果没有那么进行扩容1.5倍
    */
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        //判断是不是空数组
         if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    //扩容函数
     private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    //检测扩容后会不会超过最大值
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
    
    /*
    *删除函数(通过index删除)
    *然后数组数组整体左移(这里进行了一个是不是尾元素判断)
    */
     public E remove(int index) {
         //检查待删除下标是否合法
        rangeCheck(index);
        modCount++;
        //获取待删除的元素
        E oldValue = elementData(index);
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
        return oldValue;
    }
    /*
    *删除函数(通过对象来删除)
    *查找到object对应的index
    *下面的函数主要是通过fastRemove()来删除的
    */
      public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    //真正执行删除的函数,与通过index删除的index相同
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }
    
    /*
    *删除范围
    */
      protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }

LinkedList:底层采用双向链表实现,其实也可以当作堆或者栈来使用,线程不安全。

//属性:
//长度
transient int size = 0;
//首节点
transient Node<E> first;
//尾节点
transient Node<E> last;

//节点(采用的双向链表)
  private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
//构造方法
 //空构造方法
  public LinkedList() {}

//插入函数
/*
*头节点插入
*/
 public void addFirst(E e) {
        linkFirst(e);
    }
  private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        //判断是否是第一个节点
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }
    
    /*
    *尾节点插入
    */
     public void addLast(E e) {
        linkLast(e);
    }
     public boolean add(E e) {
        linkLast(e);
        return true;
    }
      void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        //同样判断是否为第一个节点
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    //通过头插和尾插可以看出。linkList中没有头元素(一个空元素起点)
    
 //删除函数
 /*
 *循环找到待删除的元素,源码中对待删除的元素是否为空进行了判断
 */
  public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }
     E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
        //判断删除后,他的上一个节点是否为首节点
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }
        //判断删除后,他的下一个节点是否为尾节点
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }  
    //删除首节点
     private E unlinkLast(Node<E> l) {
        final E element = l.item;
        final Node<E> prev = l.prev;
        l.item = null;
        l.prev = null; // help GC
        last = prev;
        if (prev == null)
            first = null;
        else
            prev.next = null;
        size--;
        modCount++;
        return element;
    }

    public E remove(int index) {
        //检查index是否合法
        checkElementIndex(index);
        //通过index查找到对应的值,然后删除
        return unlink(node(index));
    } 
     private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
      private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }
    //循环找到index对应的元素
     Node<E> node(int index) {
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
    /*
    *清空函数
    */
     public void clear() {
       //清空所有节点
        for (Node<E> x = first; x != null; ) {
            Node<E> next = x.next;
            x.item = null;
            x.next = null;
            x.prev = null;
            x = next;
        }
        first = last = null;
        size = 0;
        modCount++;
    }
    
    /*
    *获取函数
    */
     public E get(int index) {
         //检查index是否合法
        checkElementIndex(index);
        //循环找到index对应的对应,然后取值
        return node(index).item;
    }
    /*
    *更新函数
    */
     public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }
//除了这些函数以后还有好多对首节点,尾节点操作的函数,就不一一讨论了。
//但是下面LinkList封装了队列的操作,瞅上那么一瞅
/*
*操作函数
*/
  public void push(E e) {
        addFirst(e);
    }
    
  public E poll() {
        final Node<E> f = first;
        return (f == null) ? null : unlinkFirst(f);
    }    

Vector:底层使用数组实现 ,其他方法根ArrayList差不多,但是这个在方法上都加了synchronized锁保证了线程安全,同时也是他的效率下降。

//数据存储
protected Object[] elementData;
//数据个数
protected int elementCount;
//扩容时候的增加量
protected int capacityIncrement;
//最大个数
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//构造函数
public Vector(int initialCapacity, int capacityIncrement) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;
}
public Vector(int initialCapacity) {
    this(initialCapacity, 0);
}
public Vector() {
    this(10);
}

//添加函数
public synchronized void addElement(E obj) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = obj;
}
//检查下表是否越界
private void ensureCapacityHelper(int minCapacity) {
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
//越界之后的扩容
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

//删除函数  先查找到位置,然后删除
public synchronized boolean removeElement(Object obj) {
    modCount++;
    int i = indexOf(obj);
    if (i >= 0) {
        removeElementAt(i);
        return true;
    }
    return false;
}
//真正删除的函数
public synchronized void removeElementAt(int index) {
    modCount++;
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                 elementCount);
    }
    else if (index < 0) {
        throw new ArrayIndexOutOfBoundsException(index);
    }
    int j = elementCount - index - 1;
    if (j > 0) {
        System.arraycopy(elementData, index + 1, elementData, index, j);
    }
    elementCount--;
    elementData[elementCount] = null; /* to let gc do its work */
}
//获取函数
public synchronized E get(int index) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    return elementData(index);
}
//修改函数
public synchronized E set(int index, E element) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

MAP接口的“工具类”:储存的是键值对,键不可以重复,value可以重复,键可以为null,值不可以为null

HashMap:这个应该算是面试必备的了,面试一定会问道的,是一个散列表,采用拉链法的形式解决hash冲突,当拉链的长度大于8时,转化为红黑树,是线程 不安全的。key和value都可以为null。(transient 修饰的变量不参与序列化过程

//默认初始容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//负载因子 
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//链表最大阈值,超过这个长度会转化为红黑树
static final int TREEIFY_THRESHOLD = 8;
//当链表长度小于这个值的时候会转化为链表
static final int UNTREEIFY_THRESHOLD = 6;
//树的最小容量
static final int MIN_TREEIFY_CAPACITY = 64;
// 存储元素的数组,总是2的倍数
transient Node<k,v>[] table;
transient Set<map.entry<k,v>> entrySet;
// 存放元素的个数,注意这个不等于数组的长度。
transient int size;
// 每次扩容和更改map结构的计数器
transient int modCount;
// 临界值 当实际大小(容量*填充因子)超过临界值时,会进行扩容
int threshold;
//构造函数
public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    this.loadFactor = loadFactor;
    this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; 
}
public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}

//get方法
public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}
//hash方法
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//获取节点的方法
final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        if ((e = first.next) != null) {
            //红黑树
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            do {
            //链表    
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}
//删除
public V remove(Object key) {
    Node<K,V> e;
    return (e = removeNode(hash(key), key, null, false, true)) == null ?
        null : e.value;
}
final Node<K,V> removeNode(int hash, Object key, Object value,
                           boolean matchValue, boolean movable) {
    Node<K,V>[] tab; Node<K,V> p; int n, index;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (p = tab[index = (n - 1) & hash]) != null) {
        Node<K,V> node = null, e; K k; V v;
        //找到上边的节点
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            node = p;
        else if ((e = p.next) != null) {
            //树结构遍历
            if (p instanceof TreeNode)
                node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
            else {
            //链表结构遍历    
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key ||
                         (key != null && key.equals(k)))) {
                        //找到待删除元素     
                        node = e;
                        break;
                    }
                    p = e;
                } while ((e = e.next) != null);
            }
        }
        if (node != null && (!matchValue || (v = node.value) == value ||
                             (value != null && value.equals(v)))) {
             //树结构删除节点                    
            if (node instanceof TreeNode)
                ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
            else if (node == p)
            //如果删除的是头节点
                tab[index] = node.next;
            else
            //删除其他节点
                p.next = node.next;
            ++modCount;
            --size;
            afterNodeRemoval(node);
            return node;
        }
    }
    return null;
}

下面我们好好分析一下这个hashmap(问答形式):

1.谈谈你理解的 HashMap,讲讲其中的 get put 过程?

HashMap是一个散列表,采用拉链法解决hash冲突,当链长到达一定长度时候,转化为红黑树。

get过程:

先取到key的hash值,然后hash值与数组长度-1与操作得到数组下标值,定位到相应的数组,判断节点是树结构还是表结构,如果是树结构的话,根据树结构去寻找,如果是表节后的话,依次遍历比较key得到相应的value。

put过程:

首先通过key计算hash值,然后将hash值&length-1得到下标值,判断有无头节点,如果没有,此节点作为头节点,然后存在了,判断头节点类型,如果链表类型,那么在尾部插入,如果是树形结构那么按照树的规则插入。

他的hash函数:

//hash方法
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

2.HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标?

hashcode()返回的值是int类型 -2^31 ~ 2^31-1 ,而HashMap的范围是16~2^30,HashMap通常情况下是取不到最大值的,并且设备上也没有这个大的存储空间,导致hashcode()的值不在数组范围内,从而无法匹配存储位置。

3.HashMap怎么寻找table的下标值的解决办法?

他自己实现了hash()方法,通过两次扰动使得他自己的哈希值高低位自行进行异或运算,降低哈希冲突的概率,使数据更平均。

然后通过计算的hash值与数组的最大值进行与操作,将结果作为下标值

 

4.为什么两次扰动?

为了增大hash值的随机性,使分布更均匀,提高了对应的数组存储下标位置的随机性&均匀性,最终减少了Hash冲突,两次就够了,已经达到了高位低位同时运算的目的。

 

5为什么HashMap中String、Integer这样的包装类适合作为K?

因为包装类重写了hashcode()和equal()方法,遵守HashMap的约束规范,还有Integer和String这两次中都使用的final修饰,都是不可变的,不会存在hash值不同的情况。

6.如果在HashMap中想用自己的Object做为Key怎么办?

重写hashCode方法,因为需要计算数据的存储位置,不要试图从散列表删除一个对象来提升性能,这让虽然能更快,但是可能会导致更多的Hash碰撞。

7.table的长度为什么是2的幂次方?

只有当2的幂次方的时候,length-1的二进制值为111111这种格式,所以length-1&hash=hash%(length-1),及实现了key的定位。

并且当这种时候,在进行与操作的时候会非常快,而且如果不是2的幂次,比如15,15-1=14二进制即1110,这样1111,1101,1011,0111,1001,0101,0011,0001这几个空间永远不会取到的。这样就会增大hash冲突的概率。

这些问题只是针对于hashmap,但是更多时的时候是将他与其他“工具类进行比较”

HashTable:其实这是一个已经废弃的工具类,他也是一个hash表,采用拉链法解决hash冲突,他是线程安全的。

简单的看一下源码:

//取值 
public synchronized V get(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        //计算hash,为什么没有直接用hashcode?为什么要& 0x7FFFFFF?
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return (V)e.value;
            }
        }
        return null;
    }

//存值
 public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
          //拉链
         for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }

ConcurrentHashMap:这个也是一个特别重要的工具类,这个类是间接的实现了map接口的,线程安全的,所以也放在这里说一下。

他在1.7和1.8中实现也有很大的不同,先看在1.7中

采用了分段锁技术,其中 Segment 继承于 ReentrantLock。不会像 HashTable 那样不管是 put 还是 get 操作都需要做同步处理,理论上 ConcurrentHashMap 支持 CurrencyLevel (Segment 数组数量)的线程并发。每当一个线程占用锁访问一个 Segment 时,不会影响到其他的 Segment。因为我的环境是1.8,所以1.7的源码就不多看了。

 static class Segment<K,V> extends ReentrantLock implements Serializable {
        private static final long serialVersionUID = 2249069246763182397L;
        final float loadFactor;
        Segment(float lf) { this.loadFactor = lf; }
    }

再看看1.8中的ConcurrentHashMap ,采用CAS+Synchoried来保证并发操作,查询效率比1.7高。

//最大容量
private static final int MAXIMUM_CAPACITY = 1 << 30;
//默认长度
private static final int DEFAULT_CAPACITY = 16;
//负载因子
private static final float LOAD_FACTOR = 0.75f;
//链表转化为树的阈值
static final int TREEIFY_THRESHOLD = 8;
//树转化为链表的阈值
static final int UNTREEIFY_THRESHOLD = 6;
//存储数据
transient volatile Node<K,V>[] table;
//用来扩容或初始化的标志吧,,默认值为0,当在初始化的时候指定了大小,这会将这个大小保存在sizeCtl中,大小为数组的0.75
//当为负的时候,说明表正在初始化或扩张 -1表示初始化  -(1+n) n:表示活动的扩张线程
private transient volatile int sizeCtl;
//构造函数
public ConcurrentHashMap(int initialCapacity) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException();
    int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
               MAXIMUM_CAPACITY :
               tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
    this.sizeCtl = cap;
}
//插入
public V put(K key, V value) {
    return putVal(key, value, false);
}
final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode());
    int binCount = 0;
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        if (tab == null || (n = tab.length) == 0)
        //表为空,初始化表
            tab = initTable();
            //这个位置没有产生冲突
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            //cas插入元素
            if (casTabAt(tab, i, null,
                         new Node<K,V>(hash, key, value, null)))
                break;                   // no lock when adding to empty bin
        }
        //正在转化,当前节点也会参与转化
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            synchronized (f) {
                //如果已经冲突了,那么找到冲突位置的节点,并且是链表结构
                if (tabAt(tab, i) == f) {
                    if (fh >= 0) {
                        binCount = 1;
                        for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                            //遍历寻找与待插入key和key的hash值相同的,如果有就替换
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                                 (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            //如果没有找到,那么就插入到尾部
                            Node<K,V> pred = e;
                            if ((e = e.next) == null) {
                                pred.next = new Node<K,V>(hash, key,
                                                          value, null);
                                break;
                            }
                        }
                    }
                    //如果是树结构
                    else if (f instanceof TreeBin) {
                        Node<K,V> p;
                        binCount = 2;
                        //添加到红黑树中
                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                       value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
                }
            }
            //判断是否需要将链表转化为红黑树
            if (binCount != 0) {
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount);
    return null;
}
//取值操作
public V get(Object key) {
    Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
    int h = spread(key.hashCode());
    //找到当前的地址
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (e = tabAt(tab, (n - 1) & h)) != null) {
        if ((eh = e.hash) == h) {
            if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                return e.val;
        }
        else if (eh < 0)
            return (p = e.find(h, key)) != null ? p.val : null;
        while ((e = e.next) != null) {
            //循环遍历找到节点
            if (e.hash == h &&
                ((ek = e.key) == key || (ek != null && key.equals(ek))))
                return e.val;
        }
    }
    return null;
}
//计算hash值,这里计算hash的方法与hashtable一致
static final int spread(int h) {
    return (h ^ (h >>> 16)) & HASH_BITS;
}
//原子操作,获取指定位置节点
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
    return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
//cas原子操作,在指定位置设置值
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                    Node<K,V> c, Node<K,V> v) {
    return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
//原子操作,在指定为位置设置值
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
    U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
ConcurrentHashMap的原子操作都是navicat方法,所以没法追综。

Set接口的“工具类”:模仿了数学中集合的概念,不允许重复,只能由一个null

HashSet:底层实际上是一个hashmap的实例,是线程不安全的。

//存储对象
private transient HashMap<E,Object> map;
//作为键值对的object
private static final Object PRESENT = new Object();
//构造方法
public HashSet() {
    map = new HashMap<>();
}
public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}
public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}
//添加函数
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
//删除函数
public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
}

LinkedHashMap:hashset的子类,使用链表维护插入顺序;

这个源码很简单,但是他的原理在他的源码中体现不出来。在这给个链接,大家自己看吧。

https://segmentfault.com/a/1190000012964859

TreeSet:是一个有序集合,基于Treemap实现,使用时需要指定排序方式。

 //存储对象
private transient NavigableMap<E,Object> m;
//存储时候作为键值对的值
private static final Object PRESENT = new Object();
//存值
public boolean add(E e) {
    return m.put(e, PRESENT)==null;
}
//取值
public boolean remove(Object o) {
    return m.remove(o)==PRESENT;
}

哎,我考试都没这么准备过。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!