• No.3 常见Java集合的实现细节


    1. Set和Map

    • Set和Map的关系
      • Map 2 Set:把Map中的所有key集中起来看,就是一个Set(key不能重复,无序)(Map集合方法:set<K> keySet,返回所有key组成的Set集合)
      • Set  2  Map:将key-value捆绑在一起看(value当成key的附属物),并将这么捆绑的对放入Set中,即可实现将Set当做Map来使用。
    • HashSet和hashMap
      • HashSet:系统采用Hash算法来存储元素;HashMap:系统采用Hash算法来存储key,(value作为key的附属<Entry>)
      • 集合中存储的当然是对对象的引用啦,也即集合是多个引用变量的集合
      • HashMap:
        • 在底层将key-value对当成一个整体来处理,即一个Entry对象。
        • HashMap底层采用一个Entry[]数组(table)来保存所有的k-v对(数组元素被称为桶<bucket>)
          • transient修饰(来自stackoverflow)
            • 1.transient 是表明该数据不参与序列化。因为 HashMap 中的存储数据的数组数据成员中,数组还有很多的空间没有被使用,没有被使用到的空间被序列化没有意义。所以需要手动使用 writeObject() 方法,只序列化实际存储元素的数组。
            • 2. 由于不同的虚拟机对于相同 hashCode 产生的 Code 值可能是不一样的,如果你使用默认的序列化,那么反序列化后,元素的位置和之前的是保持一致的,可是由于 hashCode 的值不一样了,那么定位函数 indexOf()返回的元素下标就会不同,这样不是我们所想要的结果.
          • HashMap的实际容量(capacity):大于initialCapacity的、最小的2的n次方法值;
          • 极限容量:capacity*loadFactor(loadFactor为负载因子,是时间、空间成本的折中,0.75)
        • 根据Hash算法来决定Entry对象的存储位置(在哪个桶中);取Entry也是如此
        • Hash冲突时,通过“Entry链”来解决。Entry对象可以包含一个引用变量<Entry构造器中的最后一个参数>(指向下一个Entry),构成链
          • 新添加的Entry位于链的头部
      • HashSet的底层是用一个HashMap来处理的,key为Set中的元素,value存储了一个静态的Object对象
        • 向hashMap中添加key相同(hashCode和equals方法返回值都相同)的entry,将会将新的value值覆盖旧的value值;hashSet中添加相同的key时,不会覆盖,key还是原来的key(其value存储了的静态的Object对象会覆盖)(hashMap中的key也是原来的key)
          • 添加一个key相同的元素时,value会覆盖,但是key不会改变(key还是原来的key,即key不会被覆盖)
        • key相同指的是(hashcode、equals方法返回相同的元素)
        • 当将一个对象当成HashMap的key/存入HashSet,要重写hashcode、equals方法,并且两个方法的返回值保持一致。
    • TreeMap和TreeSet
      • TreeSet底层是通过TreeMap实现的
      • TreeMap底层是通过一个红黑二叉树实现的,每个Entry都是二叉树的一个节点。即TreeMap本质上讲就是一个“红黑树”,其中的每个Entry就是该红黑树的一个节点。

    2. Map 和 List

    • Map的Values()方法
      • 返回的并不是一个List集合,而是一个Values(内部类)集合对象,该集合类并不能添加元素,主要用于遍历所有value
      • 即values()方法返回的是一个不存储元素的Collection集合,当程序遍历该集合时,实际上就是遍历Map对象的value
    • Map和List的关系
      • 使用上相似,底层并没有多大的相似之处
      • 使用上:既可以说List相当于所有key都是int类型的Map,也可以说Map相当于索引是任意类型的List

    3. ArrayList和LinkedList

    • Vector——Stack;Deque(双端队列)——ArrayDeque<推荐>
    • Vector和Arraylist的区别    (共同点:实现List接口,底层是基于Java数组的)
      • Arraylist的数组用了transient修饰
        • transient修饰,序列化对象时不会直接序列化,而是可以通过方法来实现定制序列化
      • Vector线程安全,但是已基本被替代
        • 需要线程安全时,也要避免使用Vector。考虑通过Collections工具类的synchronizedList方法(有一系列包装方法)将普通Arraylist包装成线程安全的Arraylist
    • Arraylist和Linkedlist的实现差异
      • Arraylist底层数组实现:添加、删除性能差;获取指定位置元素的性能好
      • LinkedList本质上就是一个双向链表。底层通过内部类Entry实现。不涉及到具体位置情形时,性能很好。涉及到具体位置情形时,性能较差(需要挨个查找)
      • ArrayList整体性能优于LinkedList,大部分时使用ArrayList。如果经常涉及到  添加、删除,尤其经常add(E e)时,考虑LinkedList

    4.Iterator迭代器(接口)

    • “迭代器模式”。Java要求各集合都提供一个iterator()方法返回一个Iterator用于遍历该集合中的元素,至于返回的Iterator到底是哪个实现类,程序并不关心
      • 系统为遍历多种数据列表、集合、容器提供一个标准的“迭代器接口”,这些数据列表、集合、容器就可以面向相同的接口编程来访问元素。不同的数据列表、集合、容器如何实现这个“接口”,则交给它们自己去实现
    • 迭代时删除指定元素
      • 通常情况下,在进行迭代(遍历操作)时不应该进行删除(迭代时,迭代器起的主要是遍历的作用,并没有保留迭代的元素),否则ConcurrentModificationException(next()方法中的checkForModification()方法中抛出)
      • 特殊情况下遍历List集合的倒数第2个元素时,Set集合的最后一个元素时,可以删除。
      • why:List,如果此时恰好是在遍历倒数2个元素,删除元素将会使size变为size-1,遍历会提前结束(hasNext()方法确定),就不胡调用next(),因此不会抛出异常;Set,遍历最后一个元素时,本身就是最后一个,即不会再去next,因此,删除也不会异常
    PS:不足之处,欢迎指正、交流
  • 相关阅读:
    枚举代码(待更新)
    数据类型
    char
    opencv中读写视频
    数据获取与存储
    Mat 与 IplImage 和 CvMat 的转换
    Mat类的输出格式
    Mat 类的内存管理
    Mat_类
    Mat表达式
  • 原文地址:https://www.cnblogs.com/fang--/p/6185680.html
Copyright © 2020-2023  润新知