• Java 集合工具包


    Java 集合工具包

    Java集合是java提供的工具包,包含了常用的数据结构:集合、链表、队列、栈、数组、映射等。

    Java集合工具包位置是java.util.*
    Java集合主要可以划分为4个部分:

    • List列表
    • Set集合
    • Map映射
    • 工具类(Iterator迭代器、Enumeration枚举类、Arrays和Collections)。

    image

    集合的作用

    • 在类的内部,对数据进行组织;
    • 简单而快速的搜索大数量的条目;
    • 有的集合接口,提供了一系列排列有序的元素,并且可以在序列中间快速的插入或者删除有关元素;
    • 有的集合接口,提供了映射关系,可以通过关键字(key)去快速查找对应的唯一对象,而这个关键字额可以是任意类型。

    与数组的对比—————为何选择集合而不是数组

    • 数组的长度固定,集合长度可变
    • 数组只能通过下标访问元素,类型固定,而有的集合可以通过任意类型查找所映射的具体对象。

    一:集合框架

    看上面的框架图,先抓住它的主干,即Collection和Map。

    Collection接口、子接口以及实现类
    Collection接口

    • 是List、Set和Queue接口的父接口
    • 定义了可用于操作List、Set和Queue的方法-增删改查

    List接口

    • List是元素有序并且可以重复的集合,被称为序列
    • List可以精确的控制每个元素的插入位置,或删除某个位置元素
    • List接口的常用子类:
      ArrayList
      LinkedList
      Vector
      Stack

    Set接口

    • Set接口中不能加入重复元素,无序
    • Set接口常用子类:
      散列存放:HashSet
      有序存放:TreeSet

    Map和HashMap

    Map接口
    • Map提供了一种映射关系,其中的元素是以键值对(key-value)的形式存储的,能够实现根据key快速查找value
    • Map中的键值对以Entry类型的对象实例形式存在
    • 键(key值)不可重复,value值可以
    • 每个建最多只能映射到一个值
    • Map接口提供了分别返回key值集合、value值集合以及Entry(键值对)集合的方法
    • Map支持泛型,形式如:Map<K,V>
    HashMap类
    • HashMap是Map的一个重要实现类,也是最常用,基于哈希表实现
    • HashMap中的Entry对象是无序排列的
    • Key值和Value值都可以为null,但是一个HashMap只能有一个key值为null的映射(key值不可重复)

    Comparable和Comparator

    Comparable接口——可比较的
    • 实现该接口表示:这个类的实例可以比较大小,可以进行自然排序
    • 定义了默认的比较规则
    • 其实现类需要实现compareTo()方法
    • compareTo()方法返回正数表示大,负数表示小0表示相等
    Comparator接口——比较工具接口
    • 用于定义临时比较规则,而不是默认比较规则
    • 其实现类需要实现compare()方法
    • Comparable和Comparator都是Java集合框架的成员

    Iterator接口

    • 集合输出的标准操作
    • 标准做法,使用Iterator接口
    • 操作原理:
    • Iterator是专门的迭代输出接口,迭代输出就是将元素一个个进行判断,判断其是否有内容,如果有内容则把内容取出。

    image

    二:arrayList

    ArrayList继承了AbstractList,实现了List。
    构造图如下:
    蓝色线条:继承
    绿色线条:接口实现

    image

     public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    
    

    ArrayList 是一个数组队列,相当于 动态数组。与Java中的数组相比,它的容量能动态增长。它继承于AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable这些接口。

    ArrayList就是用数组实现的List容器,底层用数组实现,ArrayList包含了两个重要的对象:elementData 和 size。

    // 保存ArrayList中数据的数组
    private transient Object[] elementData;
    // ArrayList中实际数据的数量
    private int size;
    
    

    ArrayList 实现了RandmoAccess接口,即提供了随机访问功能。

    ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。

    ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。

    ArrayList支持3种遍历方式

    • (01) 第一种,通过迭代器遍历。即通过Iterator去遍历。
    • (02) 第二种,随机访问,通过索引值去遍历。
      由于ArrayList实现了RandomAccess接口,它支持通过索引值去随机访问元素。
    • (03) 第三种,for循环遍历。

    遍历ArrayList时,使用随机访问(即,通过索引序号访问)效率最高,而使用迭代器的效率最低!

    ArrayList中的操作不是线程安全的!

    所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList

    小结:

    • (01) ArrayList 实际上是通过一个数组去保存数据的。当我们构造ArrayList时;若使用默认构造函数,则ArrayList的默认容量大小是10。
    • (02) 当ArrayList容量不足以容纳全部元素时,ArrayList会重新设置容量:新的容量=“(原始容量x3)/2 + 1”。
    • (03) ArrayList的克隆函数,即是将全部元素克隆到一个数组中。
    • (04) ArrayList实现java.io.Serializable的方式。当写入到输出流时,先写入“容量”,再依次写入“每一个元素”;当读出输入流时,先读取“容量”,再依次读取“每一个元素”。

    ArrayList和LinkedList的区别

    • ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
    • 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
    • 对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。

    ArrayList和Vector的区别

    • Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。
    • Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。
    • Vector还有一个子类Stack.

    三:LinkedList

    与ArrayList一样,实现List接口,只是ArrayList是List接口的大小可变数组的实现,LinkedList是List接口链表的实现。基于链表实现的方式使得LinkedList在插入和删除时更优于ArrayList,而随机访问则比ArrayList逊色些。
    构造图如下:
    蓝色线条:继承
    绿色线条:接口实现

    image

    总结:

    • (01) LinkedList 实际上是通过双向链表去实现的。
    • 它包含一个非常重要的内部类:Entry。Entry是双向链表节点所对应的数据结构,它包括的属性有:当前节点所包含的值,上一个节点,下一个节点。
    • (02) 从LinkedList的实现方式中可以发现,它不存在LinkedList容量不足的问题。
    • (03) LinkedList的克隆函数,即是将全部元素克隆到一个新的LinkedList对象中。
    • (04) LinkedList实现java.io.Serializable。当写入到输出流时,先写入“容量”,再依次写入“每一个节点保护的值”;当读出输入流时,先读取“容量”,再依次读取“每一个元素”。
    • (05) 由于LinkedList实现了Deque,而Deque接口定义了在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null 或 false,具体取决于操作)。

    小结

    ArrayList和LinkedList的比较

    1、顺序插入速度ArrayList会比较快,因为ArrayList是基于数组实现的,数组是事先new好的,只要往指定位置塞一个数据就好了;LinkedList则不同,每次顺序插入的时候LinkedList将new一个对象出来,如果对象比较大,那么new的时间势必会长一点,再加上一些引用赋值的操作,所以顺序插入LinkedList必然慢于ArrayList

    2、基于上一点,因为LinkedList里面不仅维护了待插入的元素,还维护了Entry的前置Entry和后继Entry,如果一个LinkedList中的Entry非常多,那么LinkedList将比ArrayList更耗费一些内存

    3、数据遍历的速度,ArrayList使用最普通的for循环遍历比较快,LinkedList使用foreach循环比较快。如果各自比较,结论是:使用各自遍历效率最高的方式,ArrayList的遍历效率会比LinkedList的遍历效率高一些

    4、有些说法认为LinkedList做插入和删除更快,这种说法其实是不准确的:

    (1)LinkedList做插入、删除的时候,慢在寻址,快在只需要改变前后Entry的引用地址

    (2)ArrayList做插入、删除的时候,慢在数组元素的批量copy,快在寻址

    所以,如果待插入、删除的元素是在数据结构的前半段尤其是非常靠前的位置的时候,LinkedList的效率将大大快过ArrayList,因为ArrayList将批量copy大量的元素;越往后,对于LinkedList来说,因为它是双向链表,所以在第2个元素后面插入一个数据和在倒数第2个元素后面插入一个元素在效率上基本没有差别,但是ArrayList由于要批量copy的元素越来越少,操作速度必然追上乃至超过LinkedList。

    从这个分析看出,如果你十分确定你插入、删除的元素是在前半段,那么就使用LinkedList;如果你十分确定你删除、删除的元素在比较靠后的位置,那么可以考虑使用ArrayList。如果你不能确定你要做的插入、删除是在哪儿呢?那还是建议你使用LinkedList吧,因为一来LinkedList整体插入、删除的执行效率比较稳定,没有ArrayList这种越往后越快的情况;二来插入元素的时候,弄得不好ArrayList就要进行一次扩容。

    记住,ArrayList底层数组扩容是一个既消耗时间又消耗空间的操作。

    四:HashMap

    ArrayList、LinkedList,反映的是两种思想:

    ArrayList以数组形式实现,顺序插入、查找快,插入、删除较慢
    LinkedList以链表形式实现,顺序插入、查找较慢,插入、删除方便
    那么是否有一种数据结构能够结合上面两种的优点呢?有,答案就是HashMap。

    HashMap是基于哈希表的 Map 接口的实现,以key-value的形式存在。
    构造图如下:
    蓝色线条:继承
    绿色线条:接口实现

    image

    HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
    HashMap继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
    HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。

    HashMap的底层结构是一个数组,而数组的元素是一个单向链表

    HashMap和Hashtable的区别

    • 两者最主要的区别在于Hashtable是线程安全,而HashMap则非线程安全

    Hashtable的实现方法里面都添加了synchronized关键字来确保线程同步,因此相对而言HashMap性能会高一些,我们平时使用时若无特殊需求建议使用HashMap,在多线程环境下若使用HashMap需要使用Collections.synchronizedMap()方法来获取一个线程安全的集合(Collections.synchronizedMap()实现原理是Collections定义了一个SynchronizedMap的内部类,这个类实现了Map接口,在调用方法时使用synchronized来保证线程同步,当然了实际上操作的还是我们传入的HashMap实例,简单的说就是Collections.synchronizedMap()方法帮我们在操作HashMap时自动添加了synchronized来实现线程同步,类似的其它Collections.synchronizedXX方法也是类似原理)

    • HashMap可以使用null作为key,而Hashtable则不允许null作为key

    虽说HashMap支持null值作为key,不过建议还是尽量避免这样使用,因为一旦不小心使用了,若因此引发一些问题,排查起来很是费事

    HashMap以null作为key时,总是存储在table数组的第一个节点上

    • HashMap是对Map接口的实现,HashTable实现了Map接口和Dictionary抽象类
    • HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75

    HashMap扩容时是当前容量翻倍即:capacity2,Hashtable扩容时是容量翻倍+1即:capacity2+1

    • HashMap和Hashtable的底层实现都是数组+链表结构实现
    • 两者计算hash的方法不同

    Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取模

    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    

    HashMap计算hash对key的hashcode进行了二次hash,以获得更好的散列值,然后对table数组长度取摸

    static int hash(int h) {
            // This function ensures that hashCodes that differ only by
            // constant multiples at each bit position have a bounded
            // number of collisions (approximately 8 at default load factor).
            h ^= (h >>> 20) ^ (h >>> 12);
            return h ^ (h >>> 7) ^ (h >>> 4);
        }
    
     static int indexFor(int h, int length) {
            return h & (length-1);
        }
    

    五:TreeMap

    TreeMap是基于红黑树结构实现的一种Map,要分析TreeMap的实现首先就要对红黑树有所了解。
    构造图如下:
    蓝色线条:继承
    绿色线条:接口实现

    image

    public class TreeMap<K,V>
        extends AbstractMap<K,V>
        implements NavigableMap<K,V>, Cloneable, java.io.Serializable
    

    TreeMap 是一个有序的key-value集合,它是通过红黑树实现的。
    TreeMap 继承于AbstractMap,所以它是一个Map,即一个key-value集合。
    TreeMap 实现了NavigableMap接口,意味着它支持一系列的导航方法。比如返回有序的key集合。
    TreeMap 实现了Cloneable接口,意味着它能被克隆。
    TreeMap 实现了java.io.Serializable接口,意味着它支持序列化。

    TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
    TreeMap的基本操作 containsKey、get、put 和 remove 的时间复杂度是 log(n) 。
    另外,TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fast的。

    六:HashSet

    Set的实现类都是基于Map来实现的(HashSet是通过HashMap实现的)。
    构造图如下:
    蓝色线条:继承
    绿色线条:接口实现

    image

    public class HashSet<E>
         extends AbstractSet<E>
         implements Set<E>, Cloneable, java.io.Serializable
    

    HashSet 是一个没有重复元素的集合。
    它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素。
    HashSet是非同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:

    Set s = Collections.synchronizedSet(new HashSet(...));
    

    HashSet通过iterator()返回的迭代器是fail-fast的。

    // 底层使用HashMap来保存HashSet的元素
        private transient HashMap<E,Object> map;
        // Dummy value to associate with an Object in the backing Map
        // 由于Set只使用到了HashMap的key,所以此处定义一个静态的常量Object类,来充当HashMap的value
        private static final Object PRESENT = new Object();
    
    

    HashSet是用HashMap来保存数据,而主要使用到的就是HashMap的key。

    小结

    HashSet和HashMap、Hashtable的区别

    HashMap HashSet
    HashMap 实现了Map 接口 HashSet实现了set 接口
    HashMap 储存键值对 HashSet仅仅存储对象
    使用put()方法将元素放入map 中 使用add()方法将元素放入set 中
    HashMap中使用键对象来计算 hashCode值 HashSet使用成员对象来计算hashcode 值,对于两个对象来说hashcode 可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false
    HashMap 比较快,因为是使用唯一的键来获取对象 HashSet较HashMap 来说比较慢
  • 相关阅读:
    SpringBoot学习笔记(14)----应用监控-HTTP方式
    SpringBoot学习笔记(13)----使用Spring Session+redis实现一个简单的集群
    SpringBoot学习笔记(12)----SpringBoot实现多个 账号轮询发送邮件
    SpringBoot学习笔记(11)-----SpringBoot中使用rabbitmq,activemq消息队列和rest服务的调用
    SpringBoot学习笔记(8)-----SpringBoot文件上传
    SpringBoot学习笔记(7)-----CORS支持解决跨域问题
    设计模式:迭代器模式(Iterator)
    设计模式:适配器模式(Adapter)
    设计模式:状态模式(State)
    设计模式:抽象工厂模式(Abstract Factory)
  • 原文地址:https://www.cnblogs.com/yizhiamumu/p/16636723.html
Copyright © 2020-2023  润新知