zoukankan      html  css  js  c++  java
  • 集合类

    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

  • 相关阅读:
    Java 语句总结
    存储过程 替换字符串
    解决MyEclipse吃内存以及卡死的方法
    Tomcat启动时自动加载一个类
    oracle sql日期比较:
    项目数据库操作
    Oracle 删除重复数据只留一条
    oracle ORA-00001:违反唯一约束条件
    Oracle ORA-12519: TNS:no appropriate service handler found 解决
    tomecat 配置修改 及启动配置
  • 原文地址:https://www.cnblogs.com/SsoZhNO-1/p/14695878.html
Copyright © 2011-2022 走看看