集合类

ArrayList

线程不安全问题

  1. add方法执行的时候,可能出现脏读的问题

    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)  // s=size  elementData.length = capacity
            elementData = grow();  // 如果grow 则扩大1.5倍
        elementData[s] = e;  // 两个线程执行这一句。
        size = s + 1;
    }
    
  2. 扩充的过程可能数组越界

        private void add(E e, Object[] elementData, int s) {
            if (s == elementData.length) // 最后这个s是 size输入的,也就是说,这个地方是s
                elementData = grow();
            elementData[s] = e;
            size = s + 1;
        }
    
  3. 专门引起ConcurrentModificationException异常的modCount变量。

关于初始化

可以看出来,初始化的如果是个空对象(或者默认容量),会先指向元空间。而开辟动态数组所在的空间是在add的时候发现需要grow的时候创建的。

private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 构造函数
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;
}

LinkedList

是一个双端队列。其底层实现主要包括:

  • 内部静态类Node
    • 静态内部类可以在外部类的非静态方法下new 也可以在静态方法下new。我个人感觉是更加厉害的,而内部类必须先new一个外部类,然后.new一个内部类,这么麻烦的写法相当于杜绝了外部接触到内部类了吧。
    • 静态内部类是不能访问外部类的非静态成员
  • unlink相关:包括unlink(), unlinkFirst(), unlinkLast()底层实现,具体代码如上,实现的接口为remove,poll等的时候。通过将节点拿出双向链表,并将Node的属性设置为null,帮助GC来清除内容。
  • link相关:linkLast(), linkFirst(), linkBefore()底层实现,实现插入操作,实现的接口包括set,add,offer接口。
  • node(int index)函数:通过for循环来定位数组下标对应Node的位置,这个也是LinkedList不适合查询的原因。

ArrayList和LinkedList的在序列化上的区别(关于空间上的优化问题)

序列化是和ArrayList不同的,因为ArrayList中有elementData,其中包含了许多null地址,不应该加入序列化当中,而LinkedList的链表添加才是真正意义上的动态添加,他不需要加入了反而是first和last指针这些东西。

本质上来讲,这两个链表类型在序列化的时候需要的只有数组中的元素,而其他东西都应该是不需要序列化的,但是需要把一些辅助序列化的信息放进去。

RandomAccess

  • RandomAccess接口,标记接口,表明List提供了随机访问功能,也就是通过下标获取元素对象的功能。之所以是标记接口,是该类本来就具有某项能力,使用接口对其进行标签化,便于其他的类对其进行识别(instanceof)。
  • ArrayList数组实现,本身就有通过下标随机访问任意元素的功能。那么需要细节上注意的就是随机下标访问和顺序下标访问(LinkedList)的不同了。也就是为什么LinkedList最好不要使用循环遍历,而是用迭代器遍历的原因。
  • 实现RandomAccess同时意味着一些算法可以通过类型判断进行一些针对性优化,例子有Collections的shuffle方法。简单说就是,如果实现RandomAccess接口就下标遍历,反之迭代器遍历

红黑树

对应到java里面的数据结构就是TreeSet<>。以及TreeMap<>

    public TreeSet() {
        this(new TreeMap<>());
    }

红黑树是一种比较平衡的搜索二叉树。

搜索二叉树:左子树任何值小于根节点小于右子树任何值。且没有重复值。相同的值是可以压缩在一起的,比如加一个链表。

平衡性问题

AVL树解决了搜索二叉树倾斜问题。但是AVL树是一个高度平衡的树,需要通过左旋和右旋达到平衡的目的。当AVL树把自己左树高度和右树高度,记录在节点值里面。当他插入一个节点的时候,它会从插入一个节点开始,想回走,修改沿途的高度。当修改以后,就会发现左边的高度和右边的高度不平衡。这样就找到了一个局部,这个时候就会发生旋转。

为了平衡,AVL有四种组合,红黑树也是有五种组合。

而AVL是严格平衡树,而红黑树也好,SB树也好都把平衡性阉割到一定程度,其中红黑树就是不要超过2倍到2倍以上。这样红黑树的调整的没有那么频繁,实际上,所有的树的增删改差都是O(log n)。

TreeMap和HashMap:我们知道红黑树的原理是先查有没有,然后如果没有再put,如果有则直接返回。而HashMap则是找到put的地方如果有则替换。

Redis底层是具备多级索引结构的链表->跳表

使用红黑树(重点)

只要他的时间复杂度小于hashMap,就是红黑树的存在的意义!CRUD都是O(log n)的时间复杂度

TreeSet<Integer> treeSet = new TreeSet<Integer>(); // 红黑树是一个值,key参与排序
TreeMap<Integer,String> treeMap = new TreeMap<>(); // 红黑树里面有两个值
treeSet.add(5);
treeSet.add(10);
treeSet.add(15);
treeSet.add(20);
treeSet.add(25);

treeSet.containKey(15); // 查询有没有
// 他比哈希表强的在lastKey()cellingKey()floorKey()时间复杂度都是O(log n)而哈希是O(n)
treeSet.lastKey(); // 可以直接找到最大值。O(log n) 而哈希表是O(n)的代价
treeSet.cellingKey(12); // 如果输入数值不存在,返回刚比这个数大的数
treeSet.floorKey(12); // 找到比这个数较小的数,

HashMap

变量

变量主要包括:一个Node[]数组的table。其中Node是接口,他可能是一个单向链表。

LinkedHashMap

大多数情况下,只要不涉及线程安全问题,Map基本都可以使用HashMap,不过HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序。HashMap的这一缺点往往会带来困扰,因为有些场景,我们期待一个有序的Map。

LinkedHashMap就闪亮登场了,它虽然增加了时间和空间上的开销,但是通过维护一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序该迭代顺序可以是插入顺序或者是访问顺序。

最常见的用处LRU。

我们可以简单的认为LinkedHashMap是HashMap+LinkedList,即它既使用HashMap操作数据结构,又使用LinkedList维护插入元素的先后顺序。

由于LinkedHashMap是HashMap的子类,当JDK8中HashMap可以变成树之后,相应的LInkedHashMap也做了这些优化。

 The changes in node classes also require using two fields, (head, tail) rather than a pointer to a header node to maintain the doubly-linked before/after list. This class also previously used a different style of callback methods upon access, insertion, and removal.

ConcurrentHashMap

posted @ 2021-04-23 22:54  SsoZh  阅读(47)  评论(0编辑  收藏  举报