zoukankan      html  css  js  c++  java
  • 读书笔记-集合

    【ArrayList LinkedList Vector】

    Vector对于ArrayList,因为同步而引起的性能差别并不明显;

    LinkedList是循环双向链表,无论是否为空,总包含一个header表项:

    |——————————————————————>

    header ——> ele1 ——-> ele2 ——> ele3

              <-----       <-----         <----        
    

    <—————————————————————-|

    增加元素到表尾,ArrayList 优于 LinkedList:

    ArrayList的扩容最终调用System.arraycopy()方法;

    数组是连续的,ArrayList只有在扩容时才会低效一些;

    而链表,虽然无须扩容,但新建对象和大量赋值操作会低效;

    插入元素到任意位置,LinkedList优于ArrayList:

    因为Array的连续性,每次插入操作都需要是数组重排;

    LinkedList在任意位置插入和尾部插入是一样的;

    删除任意位置元素,ArrayList尾部删除、LinkedList在头部删除和尾部删除这三个都是高效的,其他的都是低效的:

    ArrayList任意位置删除需要重排数组;

    LinkedList删除元素并不是从头到尾找,而是头尾分别找(循环双向链表),所以只有中间元素的删除是低效的;

    迭代器本质的遍历对于ArrayList和LinkedList来说效率是一样好的:

    for-each本质就是迭代器实现,但如果不直接显式用迭代器,编译器将for-each转换为迭代器的时候总是不够聪明,导致for-each比迭代器性能略差;

    随机访问遍历,ArrayList获得最高表现,LinkedList获得最低表现;


    【HashMap Hashtable】链表root元素组成的数组

    Hashtable和HashMap的性能几乎一样,差异有:

    Hashtable是同步的,HashMap不是线程安全的;

    Hashtable不允许key或value是null,HashMap允许;

    Hashtable和HashMap的hash算法和hash值到内存的索引算法不同;

    HashMap的底层存储就是个数组,hash值索引到内存地址就是索引到数组下标:

    Object.hashCode是native方法;

    通过将hash值和数组长度按位取与直接得到数组下标;

    HashMap实际上是一个链表数组,每个数组元素又是个链表的root节点,hashCode冲突的项成链表存储:

    Entry 1 | key

    Entry 2 ——-> | value

    … | next

    Entry n | hash

    Entry被存储在数组里,重复hashCode的Entry之间又是链表链接;

    HashMap的高效源自对数组随机访问的高效,如果hashCode冲突越大,则HashMap越趋于几个链表,随机访问的性能低下;

    判断hash冲突的条件:

    e.hash == hash && (e.key == key || key.equals(e.key))

    HashMap的容量参数有两个:数组长度和负载因子;

    【负载因子】 = 元素个数 / 内部数组的总大小, 默认是0.75;如果元素个数超过了负载因子规定的阈值,则数组就会扩容;

    存储同量的元素,负载因子越大,数组空间占用越小,hash冲突越明显;


    【LinkedHashMap】此有序非彼有序:元素最后访问顺序和元素插入顺序

    所谓的HashMap无序,是说元素插入HashMap之后,由于HashCode的无序映射,使得元素遍历结果的顺序和插入的顺序不一样,从这个有序的意义上说,ArrayList, Vector, LinkedList都是有序的;

    而LinkedHashMap仅仅是实现了上述的有序,并不是TreeMap的自动排序;

    LinkedHashMap有两种次序:元素最后访问顺序和元素插入顺序:

    public LinkedHashMap(int capacity, float loadFactor, boolean accessOrder);

    accessOrder == true: 元素最后访问顺序;

    accessOrder == false: 元素插入顺序;

    元素最后访问顺序对于缓存池淘汰元素很有意义,但要慎用,以防ConcurrentModificationException,因为LinkedHashMap.get()会引起元素重排序:

    m = new LinkedHashMap<string, string="">(16, 0.75f, true);

    m.put(“1”, “aa”);

    m.put(“2”, “bb”);

    m.put(“3”, “cc”);

    Iterator iter = m.keySet().iterator();

    while(iter.hasNext()) {

    System.out.println(m.get(iter.next()));
    

    }

    【ConcurrentModificationException】 不能在迭代器模式中修改被迭代的集合,包括add(), remove(),和LinkedHashMap.get();


    【TreeMap】 自动排序、按key范围筛选的SortedMap

    TreeMap实现了SortedMap接口;

    TreeMap的性能低于HashMap;

    TreeMap是根据元素的固有顺序(Comparator和Comparable)而自动排序的,所以必须:

    1, 构造 public TreeMap(Comparator<? super K> comparator);

    或者

    2, 使用实现了Comparable的key;

    TreeMap基于红黑树实现,查找、插入、删除的复杂度是logN;

    TreeMap不止自动排序这么简单,还能做范围筛选:

    SortedMap<k,v> subMap(K fromKey, K toKey)

          返回此映射的部分视图,其键值从 fromKey(包括)到 toKey(不包括)。
    

    SortedMap<k,v> headMap(K toKey)

          返回此映射的部分视图,其键严格小于 toKey。
    

    SortedMap<k,v> tailMap(K fromKey)

          返回映射的部分视图,其键大于或等于 fromKey。
    

    【WeakHashMap】 缓存是可有也可无的

    类结构:

    Object —-> Reference

                                ---> FinalReference    default
    
                                ---> SoftReference    public                                
    
                                ---> WeakReference  public                                
    
                                ---> PhantomReference  public       
    

    局部变量是分配在栈上的,但局部变量指向的对象是分配在堆上的;

    强引用的特点:

    1, 可以直接访问目标对象;

    2, 强引用所指向的对象一直都不会被GC,会导致OOM;(引用可达GC root的情况下)

    3, 可能导致内存泄漏;

    软引用:只在堆满的情况下被GC,不然可以一直存活;(引用可达GC root的情况下);所以软引用适合用做缓存;

    弱引用:只要系统GC了,就会被GC;弱应用也适合做缓存;

    虚引用: 相当于没引用,作用在于跟踪垃圾回收过程;替代finalize进行资源清理;

    Soft和Weak引用之所以适合做缓存,是因为它们所指的对象会分别在堆满和系统GC时就被回收,从而避免OOM;

    缓存本来就是可有可无的对象,缓存中的数据丢了,还有候选方法,但如果缓存数据太大,内存就消耗太大;

    HashMap常用做缓存,但内部实现是强引用,如果put的数据过多,势必造成内存负担;

    WeakHashMap应用而生;

    WeakHashMap并不是让系统随便GC自己所持有的key-value,这些key不是它私有的,它是通过WeakReference来引用的,但系统其他地方对这些key的引用可能是强引用;

    假如WeakHashMap的key在系统内被强引用了,WeakHashMap就退化为HashMap;


    XXXSet: 对XXXMap的封装

    【HashSet】内部实现委托给一个HashMap对象;

    【LinkedHashSet】内部实现委托给一个LinkedHashMap对象;

    【TreeSet】内部实现委托给一个TreeMap对象;

    特性和性能都一样。


    【集合访问性能优化】

    1,分离循环中重复调用的代码:

    for(…; i < list.size();i++)中,size()方法被无辜多次调用;

    2, 省略相同的方法调用;

    3, 减少方法的调用:直接访问属性,而不是调用方法;

    4, 判断是否继承了【RandomAccess】而区分使用遍历方法:

    if(list instanceOf RandomAccess)

        o = list.get(i);
    

    else

        o = iter.next();
    

    ArrayList, Vector都是继承了RandomAccess的;

    RandomAccess只是个标志接口,不要让链表实现的集合实现RandomAccess,会误导客户代码;


    【Iterable】: 实现这个接口允许对象成为 “foreach” 语句的目标

    此接口定义的一个方法:

        Iterator<T> iterator()
    

    返回一个在一组 T 类型的元素上进行迭代的迭代器。

    所以说,几乎所有的集合类都得实现这个接口;

    如果是自定义的Stack、Queue,最好都实现这个接口,因为抛开队列和堆栈本身的应用场景,这些数据结构本质都是集合,很多场景下,只要求按某种方式处理集合中的每个元素,就要求这种迭代式访问了。

    foreach语句被编译成字节码之后,其实就是被转换成了一个迭代器的循环代码;

    所以说,一个集合要使用foreach,必须实现Iterator,反过来,foreach也确实是被当做迭代器处理的;

  • 相关阅读:
    分享,如何激励程序员?
    [经验交流] (最新)移动App应用安全漏洞分析报告 !
    最全最热【资源汇总】Android应用解决方案全攻略
    最赚钱十大行业 网络编辑3G工程师入选
    分享:Android Studio 导入第三方jar包,重复加载错误解决办法。
    分享:怎么去测试一个 app 是否存在安全问题?
    Android系统刷机后第一次启动很慢的原因
    转载分享:Android APP二次打包操作步骤介绍
    Android开发之HelloWorld程序
    安卓源码总体结构(2)基础知识汇总
  • 原文地址:https://www.cnblogs.com/mosthink/p/5288690.html
Copyright © 2011-2022 走看看