JAVA中的集合类

来源:互联网 发布:运动软件免费下载 编辑:程序博客网 时间:2024/06/05 23:00

集合类存放于java.util包中。
集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用(reference)。
集合类型主要有3种:set(集)、list(列表)和map(映射)。

1. List

  • 所有的List中只能容纳单个不同类型的对象组成的表,而不是Key-Value键值对。例如:[ tom,1,c ];
  • 所有的List中可以有相同的元素,例如Vector中可以有 [ tom,koo,too,koo ];
  • 所有的List中可以有null元素,例如[ tom,null,1 ];
  • 基于Array的List(Vector,ArrayList)适合查询,而LinkedList(链表)适合添加,删除操作。
  • 继承关系
    Collection<–List<–Vector
    Collection<–List<–ArrayList
    Collection<–List<–LinkedList

  • Vector:
    基于Array的List,其实就是封装了Array所不具备的一些功能方便我们使用,它不可能走出Array的限制。性能也就不可能超越Array。所以,在可能的情况下,我们要多运用Array。另外很重要的一点就是Vector是线程安全的,这个也是Vector和ArrayList的唯一的区别。

  • ArrayList:
    ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。
    每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小。默认初始容量为10。随着ArrayList中元素的增加,它的容量也会不断的自动增长。在每次添加新的元素时,ArrayList都会检查是否需要进行扩容操作,扩容操作带来数据向新数组的重新拷贝,所以如果我们知道具体业务数据量,在构造ArrayList时可以给ArrayList指定一个初始容量,这样就会减少扩容时数据的拷贝问题。当然在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。

    注意,ArrayList实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。所以为了保证同步,最好的办法是在创建时完成,以防止意外对列表进行不同步的访问:

    List list = Collections.synchronizedList(new ArrayList(...)); 

    ArrayList源码分析:
    http://blog.csdn.net/u011280067/article/details/77731729

  • LinkedList:
    LinkedList不同于前面两种List,它不是基于Array的,所以不受Array性能的限制。它每一个节点(Node)都包含两方面的内容:
    1.节点本身的数据(data);
    2.下一个节点的信息(nextNode)。
    所以当对LinkedList做添加,删除动作的时候就不用像基于Array的List一样,必须进行大量的数据移动。只要更改nextNode的相关信息就可以实现了。这就是LinkedList的优势

2. Set

  • Set实现的基础是Map(HashMap,非线程安全);
  • Set中的元素是不能重复的,如果使用add(Object obj)方法添加已经存在的对象,则会覆盖前面的对象

  • 继承关系
    Collection<–Set<–HashSet
    Collection<–Set<–HashSet<–LinkedHashSet
    Collection<–Set<–SortedSet<–TreeSet

  • HashSet:
    HashSet是基于HashMap来实现的,底层采用HashMap的key来保存元素,定义一个虚拟的 Object 对象作为 HashMap 的 value。因为是用HashMap的key存储HashSer的元素,所以HashSet中的元素不可以重复,代码如下
public class HashSet<E> extends AbstractSet<E>    implements Set<E>, Cloneable, java.io.Serializable{    static final long serialVersionUID = -5024744406713321676L;    // 初始化 HashSet,底层会初始化一个 HashMap    private transient HashMap<E,Object> map;    // 定义一个虚拟的object作为HashMap的value    private static final Object PRESENT = new Object();    // 默认构造函数     // 初始化一个空的HashMap,并使用默认初始容量为16和加载因子0.75。    public HashSet()     {        map = new HashMap<>();    }    // 构造一个包含指定 collection 中的元素的新 set。    public HashSet(Collection<? extends E> c) {          map = new HashMap<>(Math.max((int)(c.size()/.75f) + 1, 16));          addAll(c);      }      // 构造一个新的空 set    // 其底层 HashMap 实例具有指定的初始容量和指定的加载因子      public HashSet(int initialCapacity, float loadFactor)     {          map = new HashMap<>(initialCapacity, loadFactor);      }       // 构造一个新的空 set     // 其底层 HashMap 实例具有指定的初始容量和默认的加载因子(0.75)。        public HashSet(int initialCapacity)      {          map = new HashMap<>(initialCapacity);       }       // 访问权限为包权限,不对外公开的一个构造函数     // 为了子类linkedHashSet初始化时调用     // 以指定的initialCapacity和loadFactor构造一个新的空链接哈希集合。      // dummy 为标识 该构造函数主要作用是对LinkedHashSet起到一个支持作用      HashSet(int initialCapacity, float loadFactor,         boolean dummy)      {             map = new LinkedHashMap<>(initialCapacity,loadFactor);       }     // 调用map的keySet来返回所有的key    public Iterator<E> iterator() {        return map.keySet().iterator();    }    // 调用HashMap的size()方法返回Entry的数量就得到该Set里元素的个数    public int size()    {        return map.size();    }    // 调用 HashMap 的 isEmpty() 判断该 HashSet 是否为空,    // 当 HashMap 为空时,对应的 HashSet 也为空    public boolean isEmpty()    {        return map.isEmpty();    }    // 调用 HashMap 的 containsKey 判断是否包含指定 key    //HashSet 的所有元素就是通过 HashMap 的 key 来保存的    public boolean contains(Object o)    {        return map.containsKey(o);    }    // 将指定元素放入HashSet中也就是将该元素作为key 放入HashMap    public boolean add(E e)    {        return map.put(e, PRESENT) == null;    }    //调用HashMap的remove方法删除指定Entry也就删除了HashSet中对应的元素    public boolean remove(Object o)    {        return map.remove(o)==PRESENT;    }    // 调用Map的clear方法清空所有Entry也就清空了HashSet中所有元素    public void clear()    {        map.clear();    }            ......  }
  • LinkedHashSet
    LinkedHashSet是基于链表实现的,保证了存取顺序的一致,在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。
    LinkedHashSet它继承与HashSet、又基于LinkedHashMap来实现的。它的底层使用LinkedHashMap来保存所有元素,是通过调用父类的构造器底层构造一个LinkedHashMap来实现的。在相关操作上与父类HashSet的操作相同,直接调用父类HashSet的方法即可。LinkedHashSet的源代码如下:
public class LinkedHashSet<E>    extends HashSet<E>    implements Set<E>, Cloneable, java.io.Serializable {    private static final long serialVersionUID = -2851667679971038690L;    /**     * 构造一个带有指定初始容量和加载因子的新空链接哈希set。     *     * 底层会调用父类的构造方法,构造一个有指定初始容量和加载因子的LinkedHashMap实例。     * @param initialCapacity 初始容量。     * @param loadFactor 加载因子。     */    public LinkedHashSet(int initialCapacity, float loadFactor) {        super(initialCapacity, loadFactor, true);    }    /**     * 构造一个带指定初始容量和默认加载因子0.75的新空链接哈希set。     *     * 底层会调用父类的构造方法,构造一个带指定初始容量和默认加载因子0.75的LinkedHashMap实例。     * @param initialCapacity 初始容量。     */    public LinkedHashSet(int initialCapacity) {        super(initialCapacity, .75f, true);    }    /**     * 构造一个带默认初始容量16和加载因子0.75的新空链接哈希set。     *     * 底层会调用父类的构造方法,构造一个带默认初始容量16和加载因子0.75的LinkedHashMap实例。     */    public LinkedHashSet() {        super(16, .75f, true);    }    /**     * 构造一个与指定collection中的元素相同的新链接哈希set。     *      * 底层会调用父类的构造方法,构造一个足以包含指定collection     * 中所有元素的初始容量和加载因子为0.75的LinkedHashMap实例。     * @param c 其中的元素将存放在此set中的collection。     */    public LinkedHashSet(Collection<? extends E> c) {        super(Math.max(2*c.size(), 11), .75f, true);        addAll(c);    }}

在父类HashSet中,专为LinkedHashSet提供的构造方法如下,该方法为包访问权限,并未对外公开。

/**     * 以指定的initialCapacity和loadFactor构造一个新的空链接哈希集合。     * 此构造函数为包访问权限,不对外公开,实际只是是对LinkedHashSet的支持。     *     * 实际底层会以指定的参数构造一个空LinkedHashMap实例来实现。     * @param initialCapacity 初始容量。     * @param loadFactor 加载因子。     * @param dummy 标记。     */    HashSet(int initialCapacity, float loadFactor, boolean dummy) {    map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);    }
  • TreeSet
    TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向 TreeSet中加入的应该是同一个类的对象。
    TreeSet是基于TreeMap实现的,实现源代码如下:
package java.util;public class TreeSet<E> extends AbstractSet<E>    implements NavigableSet<E>, Cloneable, java.io.Serializable{  private static final long serialVersionUID = -2479143000061671589L;    /**     * The backing map.     */    private transient NavigableMap<E,Object> m;    // 定义虚拟object    private static final Object PRESENT = new Object();    /**     * 未公开的构造函数,包访问权限     */    TreeSet(NavigableMap<E,Object> m) {        this.m = m;    }    /**     * 默认构造函数,实例化一个TreeMap     */    public TreeSet() {        this(new TreeMap<E,Object>());    }    /**     * 带Comparator参数的构造函数     * 根据指定比较器进行排序     */    public TreeSet(Comparator<? super E> comparator) {        this(new TreeMap<>(comparator));    }    /**     * 带Collection的构造函数     * 按照其元素的自然顺序进行排序     */    public TreeSet(Collection<? extends E> c) {        this();        addAll(c);    }    /**     * 构造一个与指定有序 set 具有相同映射关系和相同排序的新 TreeSet     */    public TreeSet(SortedSet<E> s) {        this(s.comparator());        addAll(s);    }    /**     *     */    public Iterator<E> iterator() {        return m.navigableKeySet().iterator();    }    /**     *      */    public Iterator<E> descendingIterator() {        return m.descendingKeySet().iterator();    }    /**     *      */    public NavigableSet<E> descendingSet() {        return new TreeSet<>(m.descendingMap());    }    /**     *      */    public int size() {        return m.size();    }    /**     *      */    public boolean isEmpty() {        return m.isEmpty();    }    /**     *      */    public boolean contains(Object o) {        return m.containsKey(o);    }    /**     *      */    public boolean add(E e) {        return m.put(e, PRESENT)==null;    }    /**     *      */    public boolean remove(Object o) {        return m.remove(o)==PRESENT;    }    /**     *      */    public void clear() {        m.clear();    }    /**     * 将指定集合的元素放到set中     */    public  boolean addAll(Collection<? extends E> c) {        // Use linear-time version if applicable        if (m.size()==0 && c.size() > 0 &&            c instanceof SortedSet &&            m instanceof TreeMap) {            SortedSet<? extends E> set = (SortedSet<? extends E>) c;            TreeMap<E,Object> map = (TreeMap<E, Object>) m;            Comparator<? super E> cc = (Comparator<? super E>) set.comparator();            Comparator<? super E> mc = map.comparator();            if (cc==mc || (cc != null && cc.equals(mc))) {                map.addAllForTreeSet(set, PRESENT);                return true;            }        }        return super.addAll(c);    }        ......}

3. Map

  • 射与集或列表有明显区别,映射中每个项都是成对的。映射中存储的每个对象都有一个相关的关键字(Key)对象,关键字决定了
    对象在映射中的存储位置,检索对象时必须提供相应的关键字,就像在字典中查单词一样。关键字应该是唯一的。
  • 关键字本身并不能决定对象的存储位置,它需要对过一种散列(hashing)技术来处理,产生一个被称作散列码(hash code)的整数值
  • 散列码通常用作一个偏置量,该偏置量是相对于分配给映射的内存区域起始位置的,由此确定关键字/对象对的存储位置。理想情况
    下,散列处理应该产生给定范围内均匀分布的值,而且每个关键字应得到不同的散列码
  • HashMap:
    实现一个映象,允许存储空对象,而且允许键是空(由于键必须是唯一的,当然只能有一个)。
  • Hashtable:
    实现一个映象,所有的键必须非空。为了能高效的工作,定义键的类必须实现hashcode()方法和equal()方法。这个类 是前面java实现的一个继承,并且通常能在实现映象的其他类中更好的使用。
  • WeakHashMap:
    实现这样一个映象:通常如果一个键对一个对象而言不再被引用,键/对象对将被舍弃。这与HashMap形成对照,映象 中的键维持键/对象对的生命周期,尽管使用映象的程序不再有对键的引用,并且因此不能检索对象。
  • TreeMap:
    实现这样一个映象,对象是按键升序排列的。
原创粉丝点击