Java三大集合

一.Set

1.概述
(1)Set集合,基础自Collection。特征是插入无序,不可指定位置访问,不能有重复的数据。
(2)Set集合的实现类是基于Map集合去写的。通过内部封装Map集合来实现的比如HashSet内部封装了HashMap。
(3)Set集合的常用实现类有 HashSet,TreeSet
2.Set常用方法

方法名

描述

add (E,e)

如果set中没有存在指定的元素,则向集合里添加此元素

addAll (Collection<? extends E>,c)

如果set中没有指定collection中的所有元素,则将其添加到此set中

clear( )

移除此set中的所有元素示

contains(Object o)

如果set包含指定的元素,则返回true

containsAll (Collection<?> c)

如果此set包含指定collection 的所有元素,则返回true

equals (Qbject o)

比较指定对象与此set的相等性

hashCode( )

返回set的哈希码值

isEmpty( )

如果set不包含元素,则返回true

remove (0bject o)

如果set中存在指定的元素,则将其移除

removeAll (Collection<?> c)

移除set中那些包含在指定collection 中的元素

retainAll (Collection<?> c)

仅保留set中那些包含在指定collection 中的元素

size( )

返回set中的元素数(其容量)

toArray ( )

返回一个包含set中所有元素的数组

toArray(T[ ] a)

返回一个包含此set中所有元素的数组;返回数组的运行时类型是指定数组的类型

3.保证Set集合元素无序性
(1)保证元素唯一性需要让元素重写两个方法:一个是 hashCode(),另一个是 equals()。
(2)HashSet在存储元素的过程中首先会去调用元素的hashCode()值,看其哈希值与已经存入HashSet的元素的哈希值是否相同,如果不同 :就直接添加到集合;如果相同 :则继续调用元素的equals() 和哈希值相同的这些元素依次去比较。
(3)如果说有返回true的,那就重复不添加;如果说比较结果都说false,那就是不重复就添加。
4.HashSet与TreeSet
(1)HashSet
对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,我们应该为保存到 HashSet 中的对象覆盖 hashCode() 和 equals()。
注:下面是HashSet类的部分源码,验证Set集合元素无序性

import java.util.HashSet;
public class mydemo1 {
    public static void main(String[] args) {
        HashSet<String> hashSet=new HashSet<>();
        hashSet.add("ab");
        hashSet.add("ac");
        hashSet.add("ad");

        boolean aBoolean=hashSet.add("abc");
        boolean bBoolean=hashSet.add("abc");
        
        System.out.println(hashSet);//输出[ab, ac, ad, abc]
        System.out.println(aBoolean);//输出true,刚开始时,set集合里没有元素abc
        System.out.println(bBoolean);//输出false,向set集合里再加入元素abc时,无法成功加入
    }
}

(2)TreeSet
TreeSet是一个有序的集合,它的作用是提供有序的Set集合。它继承了AbstractSet抽象类,实现了NavigableSet,Cloneable,Serializable接口。TreeSet是基于TreeMap实现的,TreeSet的元素支持2种排序方式:自然排序或者根据提供的Comparator进行排序。
TreeSet底层是二叉树,可以对对象元素进行排序,但是自定义类需要实现comparable接口,重写comparaTo() 方法。TreeSet 可以保存对象元素的唯一性(并不是一定保证唯一性,需要根据重写的compaaTo方法来确定)
红黑树是一种自平衡二叉查找树 , 它们当中每一个节点的比较值都必须大于或等于在它的左子树中的所有节点,并且小于或等于在它的右子树中的所有节点。这确保红黑树运作时能够快速的在树中查找给定的值。
(3)HashSet与TreeSet比较
HashSet 效率要高于TreeSet,因为HashSet采用散列算法快速对集合进行增删改查 时间复杂度更是几乎接近O(1),但内部无序。
TreeSet 内部有序,可根据指定规则去排序。但效率要比较HashSet低。时间复杂度位O(log n)

二.List

1.概述
List集合代表一个元素有序,可重复的集合,集合中每个元素都有对应的顺序索引。List以特定索引来存取元素,可以有重复元素。List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector>
2.List常用的方法

方法名

描述

add(Object element)

向列表的尾部添加指定的元素

size( )

返回列表中的元素个数

get(int index)

返回列表中指定位置的元素,index从0开始

add(int index, Object element)

在列表的指定位置插入指定元素

set(int i, Object element)

将索引i位置元素替换为元素element并返回被替

clear( )

从列表中移除所有元素

isEmpty()

判断列表是否包含元素,不包含元素则返回 true,否则返回false

contains(Object o)

如果列表包含指定的元素,则返回 true

remove(int index)

移除列表中指定位置的元素,并返回被删元素

remove(Object o)

移除集合中第一次出现的指定元素,移除成功返回true,否则返回false

iterator()

返回按适当顺序在列表的元素上进行迭代的迭代器

3.ArrayList,LinkedList,Vector
(1)ArrayList
底层的数据结构是数组,数组元素类型为Object类型,即可以存放所有类型数据(可以存放null)。我们对ArrayList类的实例的所有的操作底层都是基于数组的。该类实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。ArrayList实现了RandomAccess,所以在遍历它的时候推荐使用for循环.该类是非同步的,在多线程的情况下不要使用,非线程安全。ArrayList 增长当前长度的50%,插入删除效率低。ArrayList采用数组数组实现的,查找效率比LinkedList高。
ArrayList
注:下面是ArrayList类的部分源码,是ArrayList类的部分属性

public class ArrayList<E> extends AbstractList<E>  implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    // 版本号
    private static final long serialVersionUID = 8683452581122892189L;
    // 缺省的容量
    private static final int DEFAULT_CAPACITY = 10;
    // 空对象数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    // 缺省的空对象数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    // 元素数组
    transient Object[] elementData;
    // 实际的元素大小,默认为0
    private int size;
    // 最大的数组容量
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}

(2)LinkedList
该类实现了List接口,允许有null(空)元素。LinkedList是一个双向链表,list中的每个元素,在存储自身的值之外,还额外存储了其前一个和后一个元素的地址,所以可以很方便地根据当前元素获取到其前后的元素主要用于创建链表数据结构,该类没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步,解决方法就是在创建List时候构造一个同步的List。LinkedList采用双向链表实现的,插入和删除的效率比ArrayList要高。一直在list的尾部添加元素,LinkedList效率要高。
注:下面是LinkedList类的部分源码,是LinkedList类的内部类(也是其数据结构)

private static class Node<E> {
        E item;      // 真正存储的数据
        Node<E> next;  // 前一个节点引用地址
        Node<E> prev;  // 后一个节点引用地址

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

(3)Vector
该类和ArrayList非常相似,但是该类是同步的,可以用在多线程的情况,该类允许设置默认的增长长度,默认扩容方式为原来的2倍ArrayList和Vector的区别是:ArrayList是线程不安全的,当多条线程访问同一个ArrayList集合时,程序需要手动保证该集合的同步性,而Vector则是线程安全的。Vector 实现 RandomAccess 所有提供随机访问功能,RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在Vector中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
注:下面是Vector的部分源码

public class Vector<E>
        extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    //存储数据的数组,是一个动态的数组
    protected Object[] elementData;

    //当前元素的个数
    protected int elementCount;

    //容量增长系数,扩容时使用
    protected int capacityIncrement;

    // Vector的序列版本号
    private static final long serialVersionUID = -2767605614048989439L;

    //指定数组的初始化大小,和增长系数,容量不能小于0
    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                    initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

   //指定容量,增长系数为0
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

    //采用默认容量为10,增长系数为 0
    public Vector() {
        this(10);
    }

    //使用另一个集合构造当前的集合
    public Vector(Collection<? extends E> c) {
        elementData = c.toArray();
        elementCount = elementData.length;
        // c.toArray可能(错误地)不返回Object []
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
    }
}

## 三.Map
1.概述

Map用于保存具有映射关系的数据,以键值对的方式存储数据,Map内存储的是键/值对这样以成对的对象组(可以把一组对象当成一个元素),通过“键”对象来查询“值”对象。Map集合没有继承Collection接口,其提供的是key到value的映射,Map中不能包含相同的key值,每个key只能影射一个相同的value.key值还决定了存储对象在映射中的存储位置.但不是key对象本身决定的,而是通过散列技术进行处理,可产生一个散列码的整数值,散列码通常用作一个偏移量,该偏移量对应分配给映射的内存区域的起始位置,从而确定存储对象在映射中的存储位置。Map集合常用的实现类有HashMap、HashTable、TreeMap等。
2.Map常用的方法

方法名

描述

clear( )

清空当前的map

containsKey(Object key)

判断当前map中是否包含这样的Key

containsValue(Object value)

判断判断当前map中是否包含这样的Value

entrySet( )

返回Map中所包含的键值对所组成的Set集合,每个集合元素都是Map.Entry对象

get(Object key)

返回指定key所对应的value。如果没有,这返回null

isEmpty( )

查询该Map是否为空,如果为空则返回true。

keySet( )

获取当前map中所有的Key

put(Object key, Object value)

添加一个键值对,如果当前Map中已经有一个与该key相等的键值对,则新的键值对将覆盖原来的键值对

remove(Object key)

通过key将键值对删除

size( )

返回Map里的键值对个数

values( )

获取当前集合所有的value

3.HashMap,HashTable,TreeMap
(1)HashMap
HashMap是基于哈希表的Map接口的非同步实现。实现Map接口的双列集合,数据结构是“链表散列”,也就是数组+链表 ,key唯一的value可以重复,允许存储null 键null 值,元素无序。HashMap储存的是键值对,HashMap很快。在哈希表中进行添加,删除,查找等操作,性能十分之高,不考虑哈希冲突的情况下,仅需一次定位即可完成,时间复杂度为O(1)。
注:下面是HashMap的部分源码(有参构造器)

public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    this.loadFactor = loadFactor;
    threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
    table = new Entry[capacity];
    useAltHashing = sun.misc.VM.isBooted() &&
            (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
    init();
}

(2)HashTable
Hashtable同样是基于哈希表实现的,同样每个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。Hashtable也是JDK1.0引入的类,是线程安全的,能用于多线程环境中。Hashtable同样实现了Serializable接口,它支持序列化,实现了Cloneable接口,能被克隆。Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省情况下是Synchronize的。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,但使用HashMap时就必须要自己增加同步处理。
注:下面是HashTable的部分源码(有参构造器,无参构造器)

public Hashtable(int initialCapacity, float loadFactor) {//可指定初始容量和加载因子  
        if (initialCapacity < 0)  
            throw new IllegalArgumentException("Illegal Capacity: "+  
                                               initialCapacity);  
        if (loadFactor <= 0 || Float.isNaN(loadFactor))  
            throw new IllegalArgumentException("Illegal Load: "+loadFactor);  
        if (initialCapacity==0)  
            initialCapacity = 1;//初始容量最小值为1  
        this.loadFactor = loadFactor;  
        table = new Entry[initialCapacity];//创建桶数组  
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//初始化容量阈值  
        useAltHashing = sun.misc.VM.isBooted() &&  
                (initialCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);  
    }  
    public Hashtable(int initialCapacity) {  
        this(initialCapacity, 0.75f);//默认负载因子为0.75  
    }  
    public Hashtable() {  
        this(11, 0.75f);//默认容量为11,负载因子为0.75  
    }  
    public Hashtable(Map<? extends K, ? extends V> t) {  
        this(Math.max(2*t.size(), 11), 0.75f);  
        putAll(t);  
    }

(3)TreeMap
TreeMap存储K-V键值对,通过红黑树(R-B tree)实现,TreeMap继承了NavigableMap接口,NavigableMap接口继承了SortedMap接口,支持一系列的导航定位以及导航操作的方法,当然只是提供了接口,需要TreeMap自己去实现,TreeMap实现了Cloneable接口,可被克隆,实现了Serializable接口,可序列化,TreeMap因为是通过红黑树实现,红黑树结构天然支持排序,默认情况下通过Key值的自然顺序进行排序。TreeMap可以对添加进来的元素进行排序,可以按照默认的排序方式,也可以自己来指定排序方式。
注:下面是TreeMap的部分源码(成员变量,有参构造器,无参构造器)

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
    // 比较器对象
    private final Comparator<? super K> comparator;
    // 根节点
    private transient Entry<K,V> root;
    // 集合大小
    private transient int size = 0;
    // 树结构被修改的次数
    private transient int modCount = 0;
    // 静态内部类用来表示节点类型
    static final class Entry<K,V> implements Map.Entry<K,V> {
        K key;     // 键
        V value;   // 值
        Entry<K,V> left;    // 指向左子树的引用(指针)
        Entry<K,V> right;   // 指向右子树的引用(指针)
        Entry<K,V> parent;  // 指向父节点的引用(指针)
        boolean color = BLACK; // 
    }
    
    // 1.无参构造方法
     public TreeMap() {   
        comparator = null; // 默认比较机制
    }
    
    // 2.自定义比较器的构造方法
    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }
    
	 // 3.构造已知Map对象为TreeMap
    public TreeMap(Map<? extends K, ? extends V> m) {  
        comparator = null; // 默认比较机制
        putAll(m);
    }

    // 4.构造已知的SortedMap对象为TreeMap
    public TreeMap(SortedMap<K, ? extends V> m) { 
        comparator = m.comparator(); // 使用已知对象的构造器
        try {
            buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
        } catch (java.io.IOException cannotHappen) {
        } catch (ClassNotFoundException cannotHappen) {
        }
    }
}