1 容器分类

  • 容器分为Collection集合类,和Map键值对类2种
  • 使用最多的就是第三层的容器类,其实在第三层之上还有一层Abstract 抽象类,如果要实现自己的集合类,可以继承Abstract类,而不必实现接口中的所有方法。
  1. Collection 接口
    1.  List 接口  (按插入顺序保存,元素可以重复)
      1.  ArrayList (相当于大小可变的数组,随机访问快,插入移除慢)
      2. LinkedList(插入移除快,随机访问慢,也实现了Queue接口)
    2.  set 接口(不能有重复元素)
      1.  HashSet(元素无序,查询速度非常快)
        1.  LinkedHashSet(按插入顺序保存,同时有HashSet的查询速度)
      2. TreeSet(按元素升序保存对象,或者根据元素实现的比较器排序)
    3. Queue接口(一头进一头出)

      1.  PriorityQueu(优先级高的先出,也实现了List接口)
  2. Map接口
    1.  HashMap (查找速度快,内部无规则排序)
      1.  LinkedHashMap(按插入顺序排序,查找速度快)
    2. TreeMap(按升序保存对象,或者根据元素实现的比较器排序)

2 填充容器

  • 所有Collection的构造器都可以接收另一个Collection(可以不同类型)来填充自己。

2.1 Collections

  • 数组有Arrays类填充,容器也有Collections类填充,这种工具类中一般都是静态方法不用创建它们的对象直接调用,所以很方便。
  1. fill(list, T obj)方法都只是复制一份对象的引用,并没有额外创建对象,并且只能填充List,它会将容器内的元素清空再添加元素。
  2. nCopies(int n, T o) 返回一个List 功能和fill一模一样。
  3. addAll( list, T ... obj) 将元素添加到集合,集合本身也有addAll()方法并且还可以指定位置开始添加

3 Collection

  • Collection中的方法在List和Set中都实现了,List还添加了额外的方法,如get(),这在Collection和Set中都没有,因为Set无序所以无法确定位置。

4 collection中的可选操作

  • 可选的方法就是该方法在父类中会抛出异常,如果子类不需要该方法就不必重写它,一但调用则抛出异常,如果需要就去重写它的功能。
  • Collection中的 各种添加 移除方法都是可选的。AbstractList ,AbstractSet,AbstractQueue中就是实现了可选功能,调用这些抽象类中的方法就会抛出异常。

5 List

  1. jdk1.8 中ArrayList的Add方法实现原理:  如果elementData中元素数大于10个则复制一份旧数组并扩容再将元素添加就去

      public boolean add(E e) {
    ensureCapacityInternal(size + 1); // Increments modCount!!
    //关键代码:elementData = Arrays.copyOf(elementData, newCapacity);
    elementData[size++] = e;
    return true;
    }
  2. remove方法 使用本地方法直接操作内存改变,但这也属于浅复制多线程下可能复制出错导致删不掉元素, numMoved =  size - index - 1; 最后将数组最后一个元素设为null

         System.arraycopy(elementData, index+1, elementData, index,
    numMoved);
    elementData[--size] = null; // clear to let GC do its work
  3. 值得注意的是 size是ArrayList内元素个数,并不是数组长度,ArrayList内数组长度默认最小是10

  4. LinkedList 内部是由一个内部静态类实现的一个双向链表。
     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;
    }
    }

6 Set

  1. HashSet底层是由HashMap实现的,add进去的值就put在了map中作为key值,为了减小开销,所有value值为同一个new Object() 。

    private static final Object PRESENT = new Object();
    
     public boolean add(E e) {
    return map.put(e, PRESENT)==null;
    }
  2. 保证不重复:先检测有没有相同的hash值,如果没有就添加元素,如果有在equal比较key值,相同则不允许添加,不同则允许添加入map
  3. LinkedHashSet也是由 LinkedHashMap实现
  4. TreeSet底层由TreeMap实现。TreeSet 实现了NavigableSet接口,而NavigableSet接口继承了SortedSet接口 ,NavigableSet提供了搜索功能方法,SortedSet 提供了排序功能方法,
  5. TreeSet(Comparator<? super E> comparator) ,凡是带有比较排序功能容器都有一个能传入比较器Comparator对象的构造方法,使用这个构造器可以根据自己实现的Comparator排序。

7 Queue

  • 除了并发应用外,Queue的实现只有LinkedList 和 PriorityQueue,这两个Queue的差异在于排序行为,而不是性能。队列行为在于add是添加到队头,remove移除队尾元素。
  1. PriorityQueue(Comparator<? super E> comparator) 可以使用自己的比较器比较,根据对象内某个属性设置在队列中的优先级。

8 Map

  1. HashMap的底层数据结构:

    1.    散列表(哈希表)即 数组+单链表  默认数组大小是16,数组大小一直保持是2n, 数组下标是 key的hash值进行扰乱  再与  数组大小减1  按位 做 & 运算得出。
    2.   扰乱的目的:为了使得到的值更加均匀。
    3.   减1的目的: 2n - 1 之后低位都是1,进行与运算后只要有一位不同那么就能的到不同的结果,减小了哈希冲突。同时结果范围也正好在数组大小相同。
  2. 链表的结构

    1.   jdk1.7 Enty

       static class Entry<K,V> implements Map.Entry<K,V> {
      final K key;
      V value;
      Entry<K,V> next;
      int hash;
    2.   jdk1.8 Node
      static class Node<K,V> implements Map.Entry<K,V> {
      final int hash;
      final K key;
      V value;
      Node<K,V> next;

      可以看出只是改了个名字而已。

  3. 单链表也称桶(bucket)

    1.   单链表是为了解决哈希冲突,不同的key可能计算出相同的hash值,如果哈希值冲突了那么就用头插法将新值插入链表的头,作为数组中的元素,不用尾插法能省去了遍历链表的开销。
    2.   如果key=null 那么key会一直在数组中作为链表的头。
  4. 负载因子

    1.   默认0.75 当map的桶位数(键值对数量)达到 数组容量*0.75 时就会进行扩容 ,扩容后的数组是原来的2倍。
  5. jdk1.7 扩容时的操作:

    1.   对旧链表正向遍历,遍历后插入新表,这样一来就是正向遍历,逆向插入,新表的链表顺序和旧表相反。
    2.   如果多个线程同时扩容容易循环链表,丢失一部分数据,所以多线程下put时是不安全的。
    3.   扩容后新表和旧表不一定有相同的链,也许某一部分会被拆到扩容增加的部分以减小原链长度,增加查询性能,这完全取决于Hash值。
  6. jdk1.8 扩容时的操作:

    1.   对旧链表正向遍历,检查hash值新增的高位(原来和1111做&运算,扩容后与11111做&运算,多了一位要比较的)如果是0那么还在原来数组位置,如果为1则在当前位置+扩容量 后的数组位置。
    2.   1.8 扩容后新增元素是用尾插法,所以不会出现循环,倒置链表,所以jdk1.8下put不会出现循环链表。
  7. jdk1.7 的HashMap存在的问题

    1.   有可能出现很多hash值相等的key那么数组还没有填满而,某一位置的链表非常长,put/get操作时要遍历整个链表,时间复杂度变为O(n)
  8. jdk1.8 对HashMap作出的改变:

    1.   底层数据结构变为 数组+链表+红黑树。解决了链表可能过长的问题,时间复杂度变为O(log n)
  9. jdk1.8 链表转化红黑树

    1. 限定了一些阀值,

      1.  桶的树化阀值: TREEIFY_THRESHOLD = 8 当链表长度大于8时将链表转为红黑树。
      2.  桶的链表还原阈值:UNTREEIFY_THRESHOLD = 6 当扩容后重新计算位置后若红黑树内结点少于6个则红黑树转化为链表
      3.  最小树化阀值 MIN_TREEIFY_CAPACITY = 64 只有当桶位数大于改64时才进行树化,并且以后树化不能小于 4*TREEIFY_THRESHOLD
      4.  查看红黑树
    2. 左子树右子树的判定

      1.  先通过comparableClassFor 反射获取key对象的所有接口查看有没有实现comparable接口
      2.  如果key值实现了comparable接口并且compareTo能正确执行并且key值和父结点是同一类型那么执行compareTo方法比较大小,如果大于父节点那么作为右孩子,如果小于父节点作为左孩子。
      3.  如果比较是相等的,或者没有实现接口则进入决胜局方法tieBreakOrder(),先比较他们的类名字符串如果是同一类型则类名比较就比较不出来,再调用本地方法identityHashCode()生成hash值,比较哈希值,如果哈希值相等返回-1,说明这两个对象在同一个对象,在同一块内存位置。
      4. 关于jdk1.8 的性能问题就在这里,如果Key 值没有实现comparable接口或者comparaTo方法不能的到正确结果,那么实现红黑树的性能没有只使用链表的高。
  10. get()取值:

    1.    计算出hash(key) 再通过key.equals()比较取出值
  11. 那些类做key值比较好?

    1.    String ,Integer 包装器类型,因为他们是final 型 key不会改变,不会出现放进map之后key被改变的情况,并且重写了hashCode()和equals()方法不会出现计算错误。
    2.   Object 对象做key值时要注意到上面的点。
  12. hashCode()和 equals()方法重写

    1. 重写equals方法也要重写hashCode方法这是因为 Object 中equals方法比较的是对象的地址,hashCode方法是根据对象地址生成的(字符串对象除外,相同的字符串有相同的hash值,哈希值是根据内容生成的。但字符串==仍然比较的是地址而不是哈希值)。
    2. 如果重写equals方法时是使用对象中某个条件判断他们相等,那么你再创建一个你认为相等的对象,但他两地址不一样,所以在没有重写hashCode方法后,他们的hashCode就不一样,这样存入map后使用后者取值就无法的到正确结果。
    3. 重写hashCode方法要保证 如果 equals 相同 那么hashCode一定相同,hashcode相同equals不一定相同,但这样会有哈希冲突,所以一个产生一个尽可能散列的hashCode方法非常重要。
    4. 内容相同的 字符串 内存中不止有一个,== 比较他们时 false, equals比较时为true ,哈希值相等。
  13. HashMap参考博客

    1. HashMap实现原理及源码分析
    2. HashMap在Java1.7与1.8中的区别
    3. JDK8:HashMap源码解析:comparableClassFor、compareComparables、tieBreakOrder方法
    4. JDK1.8源码阅读系列之四:HashMap (原创)
    5. 关于 HashMap 1.8 的重大更新

9 Stack和Vector

  • Vector 类可以实现可增长的对象数组。(和ArrayList相似)与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。
  • Stack是继承于Vector(矢量),由于Vector是通过数组实现的,这就意味着,Stack也是通过数组实现的而非链表
  1. Vector与ArrayList的最大区别就是Vector是线程安全的,而ArrayList不是线程安全的。另外区别还有:
    1. ArrayList不可以设置扩展的容量,默认1.5倍;Vector可以设置扩展的容量,如果没有设置,默认2倍
    2. ArrayList的无参构造方法中初始容量为0,而Vector的无参构造方法中初始容量为10。
    3. Vector线程安全,ArrayList线程不安全。
  2. Vector和它的子类Stack都是线程安全的集合。

10 接口的不同实现

  1. ArrayList底层是数组所以随机访问非常快,但添加删除时要复制数组,添加删除的代价较大。
  2. LinkedList 内部是双向链表所以插入删除代价低,对于随机访问会有顺着结点一个一个查找过程,所以速度较慢,但如果在2个端点插入或删除,会做特殊处理速度较快。
  3. TreeSet存在的唯一原因就是他可以维持元素的排序状态。

11 实用方法

  1. List的排序与查询所使用的方法与对象数组使用的方法有相同的名字和语法,Collections的static方法相当于代替了Arrays的静态方法而已。
  2. Collection 和 Map 可以设置为只读 。Collections的UnmodifiableXXX( )方法设置不同collection和Map为只读。
  3. Java容器类库的容错机制: 但遍历一个容器或者拿到了该容器的迭代器后改变容器状态如添加一个元素删除一个元素修改某个元素则会抛出异常。

12 持有引用

  • Java.lang.ref类库包含了一组类,这些类为垃圾回收提供了灵活性。
  • 三个继承Reference抽象类的类:SoftReference, WeakReference, PhantomReference,如果某个对象只能通过这三个对象才可以获得,那么GC会对这个对象作出不同的回收。
  • 这三个容器类用来保存对象的引用。
  1. 对象可获得:栈中有一个普通引用可以直接指向这个对象,或者通过不同的对象间接指向一个对象,那么这个对象就是可获得的或者可达的,可获得的对象是不能被回收的。
  2. 普通引用:也称强引用,没有被Reference包装的引用,通过普通引用可获得的对象不能被释放。
  3. 如果一个对象被普通引用指向,那么他就不能被释放,一直占据内存,如果没有引用指向那么就会被回收,如果有个对象希望以后还能访问到但是也希望内存不足时可以回收那么对这类对象的引用就可以放在Reference里
  4. Reference对象的可获得性由强到弱,越强越不容易被回收:
    1.  SoftReference 软引用 ,用来实现内存敏感的高速缓存,如果内存即将溢出时就回收对象。

        Object obj = new Object();
      SoftReference<Object> sf = new SoftReference<Object>(obj);
      obj = null;
      sf.get();//有时候会返回null
    2.  WeakReference 弱引用 用来“规范映射”而设计的,WeekHashMap中的key就是WeekReference。
    3. PhantomReference 虚引用  如果有个对象只有虚引用了那么他就会被回收。
  5. ReferenceQueue :GC时会在这种队列(可以自己创建,jvm也会自动创建)中查找虚引用,然后把虚引用的对象清理,softreference 和 weekreference 可以放也可以不放如ReferenceQueue中,但PhantomReference必须在ReferenceQueue中。
  6. WeakHashMap :key 保存弱引用,value 保存其他对象,当key弱引用指向的对象没有其他强引用引用那么key-value就会被回收。如果是普通HashMap那么key指向的对象除了多了一个HashMap的引用,还需要手动清理HashMap。
  7. 查看有关垃圾回收的知识点

知识点:

  1. 两种比较器Comparable和Comparator
    1. 一个类实现Comparable接口,重写compareTo()方法后就具有了比较能力。,至于什么跟什么比可以根据要求决定写在compareTo(Object a)方法里,

      • 如果指定的数与参数相等返回0。

      • 如果指定的数小于参数返回 -1。

      • 如果指定的数大于参数返回 1。

    2. Comparator比较器接口,可以创建自己需要的比较规则在compare(Object o1, Object o2)方法实现即可。可以对没有实现Comparable接口的类或者Comparable比较方式不符合要求的对象按自己需求比较.
      1. 1、o1大于o2,返回正整数

        2、o1等于o2,返回0

        3、o1小于o3,返回负整数

    3. Comparable.compareTo(Object a)也称自然排序,内比较器,自己内的元素排序。Comparator.compare(Object a, Object b) 外比较器,比较对象属性,无法对基本类型数组排序。
  2. 判断为空和为0非常重要,引用为空则没有指向一个实际对象,大小为0说明有对象没元素。
    Node<K,V>[] tab;
    if (tab == null || tab.length == 0)

《Java编程思想》笔记 第十七章 容器深入研究的更多相关文章

  1. java编程思想笔记(1)

    java编程思想笔记(1) 一,对象的创建和生命周期 对象的数据位于何处?怎样控制对象的生命周期? 在堆(heap)的内存池中动态地创建对象. java完全采用了动态内存分配方式. 二,垃圾回收器 自 ...

  2. Java编程思想 笔记

    date: 2019-09-06 15:10:00 updated: 2019-09-24 08:30:00 Java编程思想 笔记 1. 四类访问权限修饰词 \ 类内部 本包 子类 其他包 publ ...

  3. #Java编程思想笔记(一)——static

    Java编程思想笔记(一)--static 看<Java编程思想>已经有一段时间了,一直以来都把笔记做在印象笔记上,今天开始写博客来记录. 第一篇笔记来写static关键字. static ...

  4. 《Java编程思想》第一二章

    前段时间一直通过网络教程学习Java基础,把面向对象部分学完之后本来打算继续深入学习,但是感觉自己操之过急了,基础根本不够扎实,所以入手了一本<Java编程思想>,希望先把基础打好,再深入 ...

  5. Java编程思想 4th 第2章 一切都是对象

    Java是基于C++的,但Java是一种更纯粹的面向对象程序设计语言,和C++不同的是,Java只支持面向对象编程,因此Java的编程风格也是纯OOP风格的,即一切都是类,所有事情通过类对象协作来完成 ...

  6. 2.1(java编程思想笔记)位移操作

    java位移操作主要有两种: 有符号位移:有符号位移会保留原有数字正负性,即正数依然是正数,负数依然是负数. 有符号位左移时,低位补0. 有符号右移时:当数字为正数,高位补0.当数字为负时高位补1. ...

  7. Java编程思想 4th 第1章 对象导论

    所有编程语言都提供抽象机制. 面向对象编程似乎是一种很好的编程思想和方式,面向对象编程中的对象简洁描述是:对象具有状态.行为和标识.状态指的是数据存储,存储的数据能反应状态:行为指的是方法,方法表示对 ...

  8. Java编程思想笔记

    打好java基础 后续会增加相应基础笔试题 目录如下 1 对象导论2 一切都是对象3 操作符4 控制执行流程5 初始化与清理6 访问控制权限7 复用类8 多态9 接口10 内部类11 持有对象12 通 ...

  9. java编程思想笔记(第一章)

    Alan Kay 第一个定义了面向对象的语言 1.万物皆对象 2.程序是对象的集合,他们彼此通过发送消息来调用对方. 3.每个对象都拥有由其他对象所构成的存储 4.每个对象都拥有其类型(TYpe) 5 ...

随机推荐

  1. tomcat中配置JNDI方法

    1.项目中spring的数据源配置: <bean id="dataSource" class="org.springframework.jndi.JndiObjec ...

  2. 如何创建LocalDB数据库和数据库实例

    LocalDB是SQL Server 2012带来的新特性,它是一个专门为开发人员量身定制的轻量级数据库,下面介绍如何使用它. 创建LocalDB数据库的方法: 打开服务器资源管理器,右键点击“数据连 ...

  3. lintcode-91-最小调整代价

    91-最小调整代价 给一个整数数组,调整每个数的大小,使得相邻的两个数的差不大于一个给定的整数target,调整每个数的代价为调整前后的差的绝对值,求调整代价之和最小是多少. 注意事项 你可以假设数组 ...

  4. PokeCats开发者日志(三)

      现在是PokeCats游戏开发的第四天的晚上,明天要过周末了,所以提前写一下开发者日志吧! day4   day4主要是优化界面和增加游戏可玩性.   (1)感觉只有三只喵喵的话,玩家只需要无脑点 ...

  5. JavaScript constructor 属性详解

    对象的constructor属性用于返回创建该对象的函数,也就是我们常说的构造函数. 在JavaScript中,每个具有原型的对象都会自动获得constructor属性.除了arguments.Enu ...

  6. elasticsearch集群及filebeat server和logstash server

    elasticsearch集群及filebeat server和logstash server author:JevonWei版权声明:原创作品blog:http://119.23.52.191/ 实 ...

  7. hdu 1133 Buy the Ticket (大数+递推)

    Buy the Ticket Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)To ...

  8. [bzoj2621] [USACO12MAR]摩天大楼里的奶牛Cows in a Skyscraper

    题目链接 状压\(dp\) 根据套路,先设\(f[sta]\)为状态为\(sta\)时所用的最小分组数. 可以发现,这个状态不好转移,无法判断是否可以装下新的一个物品.于是再设一个状态\(g[sta] ...

  9. [洛谷P4238]【模板】多项式求逆

    题目大意:多项式求逆 题解:$ A^{-1}(x) = (2 - B(x) * A(x)) \times B(x) \pmod{x^n} $ ($B(x)$ 为$A(x)$在$x^{\lceil \d ...

  10. POJ 3415 后缀数组+单调栈

    题目大意: 给定A,B两种字符串,问他们当中的长度大于k的公共子串的个数有多少个 这道题目本身理解不难,将两个字符串合并后求出它的后缀数组 然后利用后缀数组求解答案 这里一开始看题解说要用栈的思想,觉 ...