一篇文章了解Java常用集合

这段时间一直在总结过去的知识,今天来总结一下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 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 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> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry entry = it.next();
            Log.d("Map", "key= " + entry.getKey() + " and value= " + entry.getValue());
        }


        /** 第三种:推荐,尤其是容量大时 通过Map.entrySet遍历key和value
         Map.entry 映射项(键-值对)  
         map.entrySet() 返回此映射中包含的映射关系的 Set视图。
         */
        for (Map.Entry 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 t) {
        throw new RuntimeException("Stub!");
    }

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

遍历方式

     Hashtable table = new Hashtable<>();
        table.put("a", 10);
        table.put("b", 20);
        table.put("c", 30);
        table.put("d", 40);

        /**
         * 方式1: 键值对遍历entrySet()
         */
        for (Map.Entry 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 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常用集合)