java 常见数据结构的解析

java 常见数据结构的解析

1:List对象

类图


List:

    有顺序的,元素可以重复;

    遍历:for,迭代;

    排序:Comparable Comparator Collections.sort()
ArrayList

    底层用数组实现的List

    特点:查询效率高,增删效率低 轻量级 线程不安全;

为什么查询快?

数组的特性是可以使用索引的方式来快速定位对象的位置,因此对于快速的随机取得对象的需求,使用ArrayList实现执行效率上会比较好;

但是数组再增加超出额外的对象时,是通过复制成新的数组来进行数组扩容,所以性能会有偏差 ;ArrayList默认使用10个

LinkedList

    底层用双向循环链表实现的List

    特点:查询效率低,增删效率高;

为什么增删效率高?

   由于链表结构的特性,每个Node都有保存向前或向后的指针

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 void add(E e) {
    checkForComodification();
    lastReturned = null;
    if (next == null)
        linkLast(e);
    else
        linkBefore(e, next);
    nextIndex++;
    expectedModCount++;
}

public void remove() {
    checkForComodification();
    if (lastReturned == null)
        throw new IllegalStateException();

    Node<E> lastNext = lastReturned.next;
    unlink(lastReturned);
    if (next == lastReturned)
        next = lastNext;
    else
        nextIndex--;
    lastReturned = null;
    expectedModCount++;
}

在执行查询时,

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}
Node<E> node(int index) {
    // assert isElementIndex(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;
    }
}

他会遍历所有元素来获取所需要的对象


注意点:

   LinkedList还实现了Deque的接口,有队列的特性,还可以用来实现任务队列

Vector:

    底层用数组实现List接口的另一个类;

    特点:重量级,占据更多的系统开销,线程安全;

2:HashMap

类图

Map

    HashMap
        底层数据结构是哈希表。线程不安全,效率高
            哈希表依赖两个方法:hashCode()和equals()
            执行顺序:
                首先判断hashCode()值是否相同
                    是:继续执行equals(),看其返回值
                        是true:说明元素重复,不添加
                        是false:就直接添加到集合
                    否:就直接添加到集合
            最终:
                自动生成hashCode()和equals()即可
        LinkedHashMap
            底层数据结构由链表和哈希表组成。
                由链表保证元素有序。
                由哈希表保证元素唯一。
    Hashtable
        底层数据结构是哈希表。线程安全,效率低
            哈希表依赖两个方法:hashCode()和equals()
            执行顺序:
                首先判断hashCode()值是否相同
                    是:继续执行equals(),看其返回值
                        是true:说明元素重复,不添加
                        是false:就直接添加到集合
                    否:就直接添加到集合
            最终:
                自动生成hashCode()和equals()即可
    TreeMap
        底层数据结构是红黑树。(是一种自平衡的二叉树)
            如何保证元素唯一性呢?
                根据比较的返回值是否是0来决定
            如何保证元素的排序呢?
                两种方式
                    自然排序(元素具备比较性)
                        让元素所属的类实现Comparable接口
                    比较器排序(集合具备比较性)
                        让集合接收一个Comparator的实现类对象

参考这一篇文章:https://blog.csdn.net/xy2953396112/article/details/54891527

HashMap的对象

            key:键的实现就是hash表,每个key都是通过计算hash来算出(hashcode()方法)

            value:值的实现是链表的结构,对同一个key多次插入即会覆盖原先的value

Hash算法的实现:

        插入时如何对key进行hash计算:

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

在get和put的过程中,计算下标时,先对hashCode进行hash操作,然后再通过hash值进一步计算

     

结论:在get和put的过程中,计算下标时,先对hashCode进行hash操作,然后再通过hash值进一步计算,即hash算法会对每个key进行hash计算得倒hash地址,再进行k-v值存储到hash表里;

如put插入的源码:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}   

默认加载因子:0.75,初始容量16,默认阈值: 8

tab为HashMap元素的个数

threshold:阈值,决定了HashMap何时扩容,以及扩容后的大小,一般等于table大小乘以loadFactor

一旦超过了这个边界就会扩容resize,1.8之后扩容有点复杂。。。。


如何获取指定的key对象:

源码:

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

先计算hash地址获取对应的节点再返回节点对象,允许为空;


------引用补充说明

要牢记以下关键点:

  • HashMap有一个叫做Entry的内部类,它用来存储key-value对。
  • 上面的Entry对象是存储在一个叫做table的Entry数组中。
  • table的索引在逻辑上叫做“桶”(bucket),它存储了链表的第一个元素。
  • key的hashcode()方法用来找到Entry对象所在的桶。
  • 如果两个key有相同的hash值,他们会被放在table数组的同一个桶里面。
  • key的equals()方法用来确保key的唯一性。
  • value对象的equals()和hashcode()方法根本一点用也没有。


ConcurrentHashMap:

在hashMap的基础上,ConcurrentHashMap将数据分为多个segment,默认16个(concurrency level),然后每次操作对一个segment加锁,避免多线程锁得几率,提高并发效率。


3:Queue

队列: 一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
          即是一种“先进先出”(FIFO—first in first out)的线性表

非线程安全的 : LinkedList类实现了Queue接口,可以把LinkedList当成Queue来用。
                          LinkedBlockingQueue为阻塞队列  

线程安全的非阻塞队列 : ConcurrentLinkedQueue


在get和put的过程中,计算下标时,先对hashCode进行hash操作,然后再通过hash值进一步计算

猜你喜欢

转载自blog.csdn.net/ailice001/article/details/80077571