美文网首页
一篇文章了解Java常用集合

一篇文章了解Java常用集合

作者: 成旭辕 | 来源:发表于2018-07-08 16:59 被阅读0次

    这段时间一直在总结过去的知识,今天来总结一下Java中常用的集合,在工作中使用最多的可能就在这里

    下面来看下Java集合中总的框架结构

    Collection系列

    image

    Map系列

    image

    这张图可以很好的了解各个集合之间的关系

    下面是最常用的集合


    image
    Java集合大致可以分为Set、List、Queue和Map四种体系

    1, Set代表无序、不可重复的集合;

    2, List代表有序、重复的集合;

    3, Map则代表具有映射关系的集合;

    4, Java 5 又增加了Queue体系集合,代表一种队列集合实现

    下面开始分别介绍四大派系

    Set系列

    Collection是父接口,Set,Queue,List都是继承Collection接口的。

    HashSet

    HashSet是Set接口的实现类,它实现了Set接口中的所有方法,并没有添加额外的方法,大多数时候使用Set集合时就是使用这个实现类。

    特点

    1,HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。

    2,不可以存放重复元素,元素存取是无序的

    3,集合元素值可以是null。

    4, HashSet不是同步的。因此是多线程访问时必须通过代码来同步

    4, HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等

    注意:HashSet底层是HashMap实现的,HashSet的所有集合元素,构成了HashMap的key,其value为一个静态Object对象。因此HashSet的所有性质,HashMap的key所构成的集合都具备

    List 集合

    List是一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。

    ArrayList和Vector

    ArrayList和Vector都是List实现类。

    ArrayList和Vector类封装了一个动态的、允许再分配的Object[]数组。ArrayList或Vector对象使用initalCapacity参数来设置该数组的长度,当向ArrayList或Vector中添加元素超过了该数组的长度时,它们的initalCapacity会自动增加。

    因此 ArrayList是一个动态扩展的数组,Vector也同样如此
    ArrayList和Vector的区别

    1, ArrayList是线程不安全的,Vector是线程安全的。

    2, Vector的性能比ArrayList差

    LinkedList

    LinkedList类也是List的实现类,so可以根据索引来随机访问集合中的元素。除此之外,LinkedList还实现了Deque接口,可以被当作成双端队列来使用

    注意:==LinkedList底层的数据结构是链表结构==

    基于链表特性,新增的一些方法

      /**
         * 将指定元素插入此列表的开头。
         */
        public void addFirst(E e) {
            throw new RuntimeException("Stub!");
        }
    
        /**
         * 将指定元素添加到此列表的结尾。
         */
        public void addLast(E e) {
            throw new RuntimeException("Stub!");
        }
    
        /**
         * 返回此列表的第一个元素
         */
        public E getFirst() {
            throw new RuntimeException("Stub!");
        }
    
        /**
         * 返回此列表的最后一个元素
         */
        public E getLast() {
            throw new RuntimeException("Stub!");
        }
    
        /**
         * 在此列表的开头插入指定的元素
         */
        public boolean offerFirst(E e) {
            throw new RuntimeException("Stub!");
        }
    
        /**
         * 在此列表末尾插入指定的元素
         */
        public boolean offerLast(E e) {
            throw new RuntimeException("Stub!");
        }
    
    
        /**
         * 获取但不移除此列表的第一个元素;如果此列表为空,则返回 null
         */
        public E peekFirst() {
            throw new RuntimeException("Stub!");
        }
    
    
        /**
         * 获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
         */
        public E peekLast() {
            throw new RuntimeException("Stub!");
        }
    
    
        /**
         * 获取并移除此列表的第一个元素;如果此列表为空,则返回 null
         */
        public E pollFirst() {
            throw new RuntimeException("Stub!");
        }
    
        /**
         * 获取并移除此列表的最后一个元素;如果此列表为空,则返回 null
         */
        public E pollLast() {
            throw new RuntimeException("Stub!");
        }
    
        /**
         * 移除并返回此列表的第一个元素。
         */
        public E removeFirst() {
            throw new RuntimeException("Stub!");
        }
    
        /**
         * 移除并返回此列表的最后一个元素
         */
        public E removeLast() {
            throw new RuntimeException("Stub!");
        }
    
        /**
         * 从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表时)
         */
        public boolean removeFirstOccurrence(Object o) {
            throw new RuntimeException("Stub!");
        }
        
        /**
         * 从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表时)
         */
        public boolean removeLastOccurrence(Object o) {
            throw new RuntimeException("Stub!");
        }
    
    
    集合 数组结构 线程安全 性能 使用场景
    List 需要保留存储顺序, 并且保留重复元素, 使用List
    ArrayList 动态数组 非线程安全 随机访问元素性能出色 查询较多, 使用ArrayList
    Vector 动态数组 线程安全 比ArrayList差 需要线程安全,使用Vector
    LinkList 链表 非线程安全 在插入、删除元素时性能比较出色但随机访问集合元素时性能较差 存取较多,使用LinkedList

    Queue集合

    Queue用户模拟队列这种数据结构,队列通常是指“先进先出”(FIFO,first-in-first-out)的容器。队列的头部是在队列中存放时间最长的元素,队列的尾部是保存在队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素。通常,队列不允许随机访问队列中的元素

    PriorityQueue

    • PriorityQueue是Queue的实现类,本质也是一个动态数组,在这一方面与ArrayList是一致的。
    • PriorityQueue中的元素可以默认自然排序(也就是数字默认是小的在队列头,字符串则按字典序排列)或者通过提供的Comparator(比较器)在队列实例化时指定的排序方式
    特点:

    PriorityQueue保存队列元素的顺序不是按加入队列的顺序,而是按队列元素的大小进行重新排序。
    所以调用peek()或pool()方法取出队列中头部的元素时,并不是取出最先进入队列的元素,而是取出队列中的最小的元素

    PriorityQueue不是线程安全的

    不允许插入 null 元素

    接下来看下Map,说到Map当然离不开他的两个重要的实现类HashMap和Hashtable

    Map集合存放具有映射关系的数据,
    因此Map集合里保存着两组数,一组值中保存Map里的key,另一组值中保存Map里的value,key和value都可以是任何引用类型的数据。

    特点:Map的key不允许重复,即同一个Map对象中任何两个key通过equals方法比较返回的一定是false

    HashMap

    HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。

    理解HashMap本质

    
     public HashMap() {
            throw new RuntimeException("Stub!");
        }
        
     public HashMap(int initialCapacity) {
            throw new RuntimeException("Stub!");
        }
        
     public HashMap(int initialCapacity, float loadFactor) {
            throw new RuntimeException("Stub!");
        }
    
     public HashMap(Map<? extends K, ? extends V> m) {
            throw new RuntimeException("Stub!");
        }
        
    

    构造函数中有两个重要的参数:容量大小(initialCapacity)和加载因子(loadFactor)

    • 容量(initialCapacity)是: 哈希表的容量,初始容量是哈希表在创建时的容量(即DEFAULT_INITIAL_CAPACITY = 1 << 4)

    • 加载因子(loadFactor): 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 resize操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。默认加载因子是 0.75(即DEFAULT_LOAD_FACTOR = 0.75f),

    注:

    HashMap是通过"拉链法"实现的哈希表。它包括几个重要的成员变量:table, size, threshold, loadFactor。

    table是一个Node[]数组类型,而Node实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Node数组中的。

    size是HashMap的大小,它是HashMap保存的键值对的数量。

    threshold是HashMap的阈值,用于判断是否需要调整HashMap的容量。threshold的值="容量*加载因子",当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。

    loadFactor 为加载因子

    HashMap遍历方式

        Map<Integer, String> map = new HashMap<>();
            map.put(1, "acfgdws");
            map.put(2, "bwdfrx");
            map.put(3, "abrgcs");
            map.put(4, "absrfgr");
            map.put(5, "abcderf");
    
            /**
             *  第一种:通过Map.keySet遍历key和value
             */
            for (Integer key : map.keySet()) { //map.keySet()返回的是所有key的值
                String str = map.get(key);//得到每个key多对用value的值
                Log.d("Map", key + "     " + str);
            }
    
            /**
             *第二种:通过Map.entrySet使用iterator遍历key和value
             */
            Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<Integer, String> entry = it.next();
                Log.d("Map", "key= " + entry.getKey() + " and value= " + entry.getValue());
            }
    
    
            /** 第三种:推荐,尤其是容量大时 通过Map.entrySet遍历key和value
             Map.entry<Integer,String> 映射项(键-值对)  
             map.entrySet() 返回此映射中包含的映射关系的 Set视图。
             */
            for (Map.Entry<Integer, String> entry : map.entrySet()) {
                Log.d("Map", "key= " + entry.getKey() + " and value= " + entry.getValue());
            }
    
            /**
             * 第四种:通过Map.values()遍历所有的value,但不能遍历key
             */
            for (String v : map.values()) {
                Log.d("Map", "value= " + v);
            }
    

    Hashtable

    是一个散列表,它存储的内容是键值对(key-value)映射

    构造函数

     public Hashtable() {
            throw new RuntimeException("Stub!");
        }
        
          public Hashtable(int initialCapacity) {
            throw new RuntimeException("Stub!");
        }
        
      public Hashtable(int initialCapacity, float loadFactor) {
            throw new RuntimeException("Stub!");
        }
    
        public Hashtable(Map<? extends K, ? extends V> t) {
            throw new RuntimeException("Stub!");
        }
    

    从构造函数可以看出和HashMap一样有两个重要的参数:容量大小(initialCapacity)和加载因子(loadFactor)

    遍历方式

         Hashtable<String, Integer> table = new Hashtable<>();
            table.put("a", 10);
            table.put("b", 20);
            table.put("c", 30);
            table.put("d", 40);
    
            /**
             * 方式1: 键值对遍历entrySet()
             */
            for (Map.Entry<String, Integer> entry : table.entrySet()) {
                String key = entry.getKey();
                int value = entry.getValue();
                Log.d("Hashtable", "entrySet:" + key + " " + value);
            }
    
    
            /***
             * 方式2: key键的遍历
             */
            for (String key : table.keySet()) {
                int value = table.get(key);
                Log.d("Hashtable", "keySet:" + key + " " + value);
            }
    
    
            /**
             * 方式3: 通过Enumeration来遍历Hashtable
             */
            Enumeration<String> enu = table.keys();
            while (enu.hasMoreElements()) {
                Log.d("Hashtable", "Enumeration:" + table.keys() + " " + enu.nextElement());
            }
    
    HashMap与Hashtable的区别
    集合类型 数据结构 基类 线程安全 性能 key或value是否可以为null
    HashMap 哈希表来存储键值对 AbstractMap 线程安全 比Hashtable性能好 可以
    Hashtable 哈希表来存储键值对 Dictionary 不安全 多个线程访问同一个map性能好 不可以
    注:

    Dictionary是什么?它是任何可将键映射到相应值的类的抽象父类,而AbstractMap是基于Map接口的骨干实现,它以最大限度地减少实现此接口所需的工作

    补充:HashMap仅支持Iterator的遍历方式,Hashtable支持Iterator和Enumeration两种遍历方式。

    相关文章

      网友评论

          本文标题:一篇文章了解Java常用集合

          本文链接:https://www.haomeiwen.com/subject/wwvouftx.html