`
Jameslyy
  • 浏览: 391405 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java 中的集合类

阅读更多

一、Jdk集合框架

Jdk集合框架主体结构图显示了Jdk集合框架类结构。Jdk集合由collection接口牵头,Set、List和Map承担主角,用数据结构Tree,Array,Hash,Link作为具体实现手段,组成了一个在功能和性能上相当不错的框架。整个框架由主体结构和辅助结构组成,主体结构形成了框架基础,辅助结构为使用框架提供了便利。

 

jdk的集合框架的主体结构:

接口 简述 实现 操作特性 成员要求
Set 成员不能重复 HashSet 外部无序地遍历成员。 成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
TreeSet 外部有序地遍历成员;附加实现了SortedSet, 支持子集等要求顺序的操作 成员要求实现caparable接口,或者使用 Comparator构造TreeSet。成员一般为同一类型。
LinkedHashSet 外部按成员的插入顺序遍历成员 成员与HashSet成员类似
List 提供基于索引的对成员的随机访问 ArrayList 提供快速的基于索引的成员访问,对尾部成员的增加和删除支持较好 成员可为任意Object子类的对象
LinkedList 对列表中任何位置的成员的增加和删除支持较好,但对基于索引的成员访问支持性能较差 成员可为任意Object子类的对象
Map 保存键值对成员,基于键找值操作,compareTo或compare方法对键排序 HashMap 能满足用户对Map的通用需求 键成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
TreeMap 支持对键有序地遍历,使用时建议先用HashMap增加和删除成员,最后从HashMap生成TreeMap;附加实现了SortedMap接口,支持子Map等要求顺序的操作 键成员要求实现caparable接口,或者使用Comparator构造TreeMap。键成员一般为同一类型。
LinkedHashMap 保留键的插入顺序,用equals 方法检查键和值的相等性 成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
IdentityHashMap 使用== 来检查键和值的相等性。 成员使用的是严格相等
WeakHashMap 其行为依赖于垃圾回收线程,没有绝对理由则少用  

  • facade类──Collections类

    Collections类包含了对框架中集合的普遍性操作,充当fa?ade。这些操作都被实现为静态函数,分为几下几类:

    1. 最大最小函数,对于某个集合,寻找最大最小成员(或键成员),要求成员都必须实现comparable接口;
    2. 生成不可更改的singleton集合(singleton集合为包含一个成员的集合);
    3. 对list进行排序、重组、倒序操作;
    4. 对list进行填充、批量置换、和快速搜索操作;
    5. 对集合进行同步化,以便适应多线程环境;
    6. 对集合进行不可更改修饰,返回不可更改的集合视图。
  • 等值方法一 ──equals(Object o)方法

    equals方法实现了一个等价关系:

    • 自反性: 对于任何对象x, x.equals(x)必须返回true;
    • 对称性: 对于任何对象x和y, x.equals(y)==true 当切仅当y.equals(x)==true;
    • 传递性: 对于任何对象x,y,z,如果x.equals(y)==true和y.equals(z)==true,则x.equals(z)==true;
    • 一致性: 对于任何对象x和y,只要没有对x,y进行修改,多次的x.equals(y) 调用应该返回相同的值;
    • 对于任何non-null 对象x, x.equals(null)==false。

    Object类中这个方法被实现为严格相等,也就是如果比较的两个对象是同一对象,即==操作为true,则返回true。所以如果想在集合中搜索内容相等的成员对象或在map中获取内容相等的键映射的值,成员对象(或键对象)的类必须覆盖equals(Object o)方法。

  • 等值方法二──hashCode()方法

    这个方法返回一个对象的hash 代码,在hashtable数据结构实现的集合中,它决定了这个对象被放到哪个bucket中。而equals方法则是进一步到bucket寻找具体对象的依据。

    为了hashtable数据结构能正常工作,hashCode方法的通用协议是:equals方法定为相等的两个对象的hashCode()返回的整数一定相同。只有这样才能在hashtable数据结构中找到这个对象。而equals方法定为不相等的两个对象的hashCode()返回的整数可以不相同,如果相同则为哈西冲突。所以尽量保证equals方法定为不相等的对象的hashCode()返回的整数不相同。

    类Object实现的hashCode使用对象的内部地址转化为整数返回,使得o1.equals(o2)当且仅当o1.hashCode()== o2.hashCode()。

  • 比较接口一──接口Comparable

    这个接口要求全序,也叫自然排序,实现了这个接口的类的compareTo称为自然比较方法。

    Collections.sort方法能对一个实现了这个接口的类的对象的列表自动排序。在没有指定comparator的情况下,这样的对象同时也能作为排序map的键或排序set的成员。

    如果一个类的任意两个实例e1和e2,(e1.compareTo((Object)e2) == 0)和e1.equals((Object)e2)有同样的逻辑值,我们就说这个类的自然排序和equals方法一致。另外e1.compareTo(null)应该抛出NullPointerException。

    强烈要求保持这种一致性,否则没有显示指定comparator的排序set(或map)将会破坏set (或 map)的普遍接口协议,因为这些接口协议用equals方法决定成员身份。

    compareTo方法的协议为:

    1. 当e1 < e2 时(e1.compareTo((Object)e2) <0;
    2. 当e1 > e2 时(e1.compareTo((Object)e2)〉0;
    3. 当e1.equals(e2) 时(e1.compareTo((Object)e2) ==0
    4. 当两个对象无法比较时,应该抛出ClassCastException 异常。

    在j2sdk1.4中,下面的类实现了这个接口:

    BigDecimal, BigInteger, Byte, ByteBuffer, Character, CharBuffer, Charset, CollationKey, Date, Double, DoubleBuffer, File, Float, FloatBuffer, IntBuffer, Integer, Long, LongBuffer, ObjectStreamField, Short, ShortBuffer, String, URI。

    二、常见集合接口和集合类

    java 代码
    1. import java.util.List;   
    2. import java.util.ArrayList;   
    3. import java.util.LinkedList;   
    4.   
    5. import java.util.Map;   
    6. import java.util.HashMap;   
    7. import java.util.LinkedHashMap;   
    8. import java.util.TreeMap;
    9.   
    10. import java.util.Set;   
    11. import java.util.HashSet;   
    12. import java.util.LinkedHashSet;   
    13. import java.util.TreeSet;
    14.   
    15. import java.util.Vector;   
    16. import java.util.Hashtable;  

    1、java.util.ArrayList

  • List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)

    sizeisEmptygetsetiteratorlistIterator 操作都以固定时间运行。add 操作以分摊的固定时间 运行,也就是说,添加 n 个元素需要 O(n) 时间。其他所有操作都以线性时间运行(大体上讲)。与用于 LinkedList 实现的常数因子相比,此实现的常数因子较低。

    每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。

    在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。

    注意,此实现不是同步的。如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:

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

    此类的 iteratorlistIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。

    注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。

    此类是 Java Collections Framework 的成员。

    Since: 1.2

    2、java.util.LinkedList

    List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 getremoveinsert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列 (deque)。

    此类实现 Queue 接口,为 addpoll 等提供先进先出队列操作。其他堆栈和双端队列操作可以根据标准列表操作方便地进行再次强制转换。虽然它们可能比等效列表操作运行稍快,但是将其包括在这里主要是出于方便考虑。

    所有操作都是按照双重链接列表的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。

    注意,此实现不是同步的。如果多个线程同时访问列表,而其中至少一个线程从结构上修改了该列表,则它必须 保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,如下所示:

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

    此类的 iteratorlistIterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果从结构上对列表进行修改,除非通过迭代器自身的 removeadd 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。

    注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何硬性保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

    此类是 Java Collections Framework 的成员。

    Since: 1.2

    3、Vector 类提供了实现可增长数组的功能,随着更多元素加入其中,数组变的更大。在删除一些元素之后,数组变小。

  • java.util.Vector<e></e>

    java.lang.Object
      继承者 java.util.AbstractCollection<e></e>
          继承者 java.util.AbstractList<e></e>
              继承者 java.util.Vector<e></e>
    
    所有已实现的接口:
    Serializable, Cloneable, Iterable<e></e>, Collection<e></e>, List<e></e>, RandomAccess
    直接子类:Stack

    Vector 类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。

    每个向量会试图通过维护 capacitycapacityIncrement 来优化存储管理。capacity 始终至少应与向量的大小相等;这个值通常比后者大些,因为随着将组件添加到向量中,其存储将按 capacityIncrement 的大小增加存储块。应用程序可以在插入大量组件前增加向量的容量;这样就减少了增加的重分配的量。

    从 Java 2 平台 v1.2 开始,已改进此类以实现 List,这样它就成为了 Java 的集合框架的一部分。与新集合的实现不同,Vector 是同步的。

    由 Vector 的 iterator 和 listIterator 方法所返回的迭代器是快速失败的:如果在迭代器创建后的任意时间从结构上修改了向量(通过迭代器自身的 remove 或 add 方法之外的任何其他方式),则迭代器将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就完全失败,而不是冒着在将来不确定的时间任意发生不确定行为的风险。Vector 的 elements 方法返回的 Enumeration 不是 快速失败的。

    注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。
      Vector 有三个构造函数:

      public Vector(int initialCapacity,int capacityIncrement)
      public Vector(int initialCapacity)
      public Vector()

      Vector 运行时创建一个初始的存储容量initialCapacity,存储容量是以capacityIncrement 变量定义的增量增长。初始的存储容量和capacityIncrement 可以在Vector 的构造函数中定义。第二个构造函数只创建初始存储容量。第三个构造函数既不指定初始的存储容量也不指定capacityIncrement。

      Vector 类提供的访问方法支持类似数组运算和与Vector 大小相关的运算。类似数组的运算允许向量中增加,删除和插入元素。它们也允许测试矢量的内容和检索指定的元素,与大小相关的运算允许判定字节大小和矢量中元素不数目。

    4、java.util.Hashtable

    java.lang.Object
      继承者 java.util.Dictionary
          继承者 java.util.Hashtable

    所有已实现的接口:
    Serializable, Cloneable, Map
    直接已知子类:
    Properties, UIDefaults

    此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。

    为了成功地在哈希表中存储和检索对象,用作键的对象必须实现 hashCode 方法和 equals 方法。

    Hashtable 的实例有两个参数影响其性能:初始容量加载因子容量 是哈希表中 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为 open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。加载因子 是对哈希表在其容量自动增加之前可以达到多满的一个尺度。初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。

    通常,默认加载因子(.75)在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查找某个条目的时间(在大多数 Hashtable 操作中,包括 getput 操作,都反映了这一点)。

    初始容量主要控制空间消耗与执行 rehash 操作所需要的时间损耗之间的平衡。如果初始容量大于 Hashtable 所包含的最大条目数除以加载因子,则永远 不会发生 rehash 操作。但是,将初始容量设置太高可能会浪费空间。

    如果很多条目要存储在一个 Hashtable 中,那么与根据需要执行自动 rehashing 操作来增大表的容量的做法相比,使用足够大的初始容量创建哈希表或许可以更有效地插入条目。

    下面这个示例创建了一个数字的哈希表。它将数字的名称用作键:

         Hashtable numbers = new Hashtable();
         numbers.put("one", new Integer(1));
         numbers.put("two", new Integer(2));
         numbers.put("three", new Integer(3));

    要检索一个数字,可以使用以下代码:

       Integer n = (Integer)numbers.get("two");
          if (n != null) {
                System.out.println("two = " + n);
          }

    自 Java 2 平台 v1.2 以来,此类已经改进为可以实现 Map,因此它变成了 Java Collections Framework 的一部分。与新集合的实现不同,Hashtable 是同步的。

    由迭代器返回的 Iterator 和由所有 Hashtable 的“collection 视图方法”返回的 Collection 的 listIterator 方法都是快速失败 的:在创建 Iterator 之后,如果从结构上对 Hashtable 进行修改,除非通过 Iterator 自身的移除或添加方法,否则在任何时间以任何方式对其进行修改,Iterator 都将抛出 ConcurrentModificationException。因此,面对并发的修改,Iterator 很快就会完全失败,而不冒在将来某个不确定的时间发生任意不确定行为的风险。由 Hashtable 的键和值方法返回的 Enumeration 是快速失败的。

    注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误做法:迭代器的快速失败行为应该仅用于检测程序错误。

    此类是 Java Collections Framework 的成员。

    5、java.util.HashMap

    java.lang.Object
      继承者 java.util.AbstractMap
          继承者 java.util.HashMap
    
    所有已实现的接口:Serializable, Cloneable, Map
    直接已知子类:LinkedHashMap, PrinterStateReasons

    基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

    此实现假定哈希函数将元素正确分布在各桶之间,可为基本操作(getput)提供稳定的性能。迭代集合视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)的和成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

    HashMap 的实例有两个参数影响其性能:初始容量加载因子容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,通过调用 rehash 方法将容量翻倍。

    通常,默认加载因子 (.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 getput 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地降低 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。

    如果很多映射关系要存储在 HashMap 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。

    注意,此实现不是同步的。如果多个线程同时访问此映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。(结构上的修改是指添加或删除一个或多个映射关系的操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示:

     Map m = Collections.synchronizedMap(new HashMap(...));
    

    由所有此类的“集合视图方法”所返回的迭代器都是快速失败 的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的 removeadd 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒在将来不确定的时间任意发生不确定行为的风险。

    注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

    此类是 Java Collections Framework 的成员。

    6、java.util.TreeMap

    java.lang.Object
      继承者 java.util.AbstractMap
          继承者 java.util.TreeMap
    
    所有已实现的接口:Serializable, Cloneable, Map, SortedMap

    SortedMap 接口的基于红黑树的实现。此类保证了映射按照升序顺序排列关键字,根据使用的构造方法不同,可能会按照键的类的自然顺序 进行排序(参见 Comparable),或者按照创建时所提供的比较器进行排序。

    此实现为 containsKeygetputremove 操作提供了保证的 log(n) 时间开销。这些算法是 Cormen、Leiserson 和 Rivest 的《Introduction to Algorithms》中的算法的改编。

    注意,如果有序映射要正确实现 Map 接口,则有序映射所保持的顺序(无论是否明确提供比较器)都必须保持与等号一致。(请参见与等号一致 的精确定义的 ComparableComparator。)这也是因为 Map 接口是按照等号操作定义的,但映射使用它的 compareTo(或 compare)方法对所有键进行比较,因此从有序映射的观点来看,此方法认为相等的两个键就是相等的。即使顺序与等号不一致,有序映射的行为仍然 定义良好的;只不过没有遵守 Map 接口的常规约定。

    注意,此实现不是同步的。如果多个线程同时访问一个映射,并且其中至少一个线程从结构上修改了该映射,则其必须 保持外部同步。(结构上修改是指添加或删除一个或多个映射关系的操作;仅改变与现有键关联的值不是结构上的修改。)这一般通过对自然封装该映射的某个对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示:

         Map m = Collections.synchronizedMap(new TreeMap(...));
    

    由所有此类的“collection 视图方法”所返回的迭代器都是快速失败 的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的 removeadd 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就完全失败,而不是冒着在将来不确定的时间任意发生不确定行为的风险。

    注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。

    此类是Java Collections Framework 的成员。

    从以下版本开始: 1.2

    7、java.util.TreeSet<e></e>

    java.lang.Object
      继承者 java.util.AbstractCollection<e></e>
          继承者 java.util.AbstractSet<e></e>
              继承者 java.util.TreeSet<e></e>
    
    所有已实现的接口:Serializable, Cloneable, Iterable<e></e>, Collection<e></e>, Set<e></e>, SortedSet<e></e>

    此类实现 Set 接口,该接口由 TreeMap 实例支持。此类保证排序后的 set 按照升序排列元素,根据使用的构造方法不同,可能会按照元素的自然顺序 进行排序(参见 Comparable),或按照在创建 set 时所提供的比较器进行排序。

    此实现为基本操作(addremovecontains)提供了可保证的 log(n) 时间开销。

    注意,如果要正确实现 Set 接口,则 set 所维护的顺序(是否提供了显式比较器)必须为与等号一致(请参阅与等号一致 精确定义的 ComparableComparator)。这是因为 Set 接口根据 equals 操作进行定义,但 TreeSet 实例将使用其 compareTo(或 compare)方法执行所有的键比较,因此,从 set 的角度出发,该方法认为相等的两个键就是相等的。即使 set 的顺序与等号不一致,其行为也 定义良好的;它只是违背了 Set 接口的常规协定。

    注意,此实现不是同步的。如果多个线程同时访问一个 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。通常通过对某个自然封装该 set 的对象进行同步来实现此操作。如果不存在此类对象,则 set 就应该使用 Collections.synchronizedSet 方法进行“包装”。此操作最好在创建时进行,以防止对 set 的意外非同步访问:

         SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));

    此类的 iterator 方法返回的迭代器是快速失败的:如果在迭代器创建后的任意时间修改 set(通过迭代器本身 remove 方法之外的任何其他方式),迭代器将抛出 ConcurrentModificationException。因此,在并发修改时,迭代器将快速而彻底地失败,而不会在以后的不确定时间有出现任意、无法确定行为的危险。

    注意,无法保证迭代器的快速失败行为,因为通常来说,不可能在非同步并发修改的情况下提供任何硬性保证。快速失败的迭代器将尽量抛出 ConcurrentModificationException。因此,为了获得其准确性而编写依赖此异常的程序的做法是错误的:迭代器的快速失败行为应当仅用于检测 bug。

    此类是Java Collections Framework 的成员。

    从以下版本开始:1.2
    8、java.util.HashSet<e></e>
    java.lang.Object
      继承者 java.util.AbstractCollection<e></e>
          继承者 java.util.AbstractSet<e></e>
              继承者 java.util.HashSet<e></e>
    
    所有已实现的接口:
    Serializable, Cloneable, Iterable<e></e>, Collection<e></e>, Set<e></e>
    直接已知子类:
    JobStateReasons, LinkedHashSet

    此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证集合的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。

    此类为基本操作提供了稳定性能,这些基本操作包括 addremovecontainssize,假定哈希函数将这些元素正确地分布在桶中。对此集合进行迭代所需的时间与 HashSet 实例的大小(元素的数量)和底层 HashMap 实例(桶的数量)的“容量”的和成比例。因此,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

    注意,此实现不是同步的。 如果多个线程同时访问一个集合,而其中至少一个线程修改了该集合,那么它必须 保持外部同步。这通常是通过对自然封装该集合的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装”集合。最好在创建时完成这一操作,以防止对 HashSet 实例进行意外的不同步访问:

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

    此类的 iterator 方法返回的迭代器是快速失败 的:在创建迭代器之后,如果对集合进行修改,除非通过迭代器自身的 remove 方法,否则在任何时间以任何方式对其进行修改,Iterator 都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来在某个不确定时间发生任意不确定行为的风险。

    注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器在尽最大努力抛出 ConcurrentModificationException。因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误做法:迭代器的快速失败行为应该仅用于检测程序错误。

    此类是Java Collections Framework 的成员。

    从以下版本开始:1.2

    三、之间的区别

    1、Vector类和ArrayList类

    主要不同之处在于同步。Vector是线程安全的,但ArrayList不是。这使得ArrayList要比Vector快速,其性能也更好一些。

    当需要增长时,Vector默认增长为原来一培,而ArrayList却是原来的一半。

    通过索引访问和更新元素时,Vector和ArrayList的实现有着卓越的性能,因为不存在除范围检查之外的其他开销。除非内部数组空间耗尽必须进行扩展,否则,向列表的末尾添加元素或者从列表的末尾删除元素时,都同样有着优秀的性能。插入元素和删除元素总是要进行数组复制(当数组先必须进行扩展时,需要两次复制)。被复制元素的数量和[size-index]成比例,即和插入/删除点到集合中最后索引位置之间的距离成比例。对于插入操作,把元素插入到集合最前面(索引0)时性能最差,插入到集合最后面时(最后一个现有元素之后)时性能最好。随着集合规模的增大,数组复制的开销也迅速增加,因为每次插入操作必须复制的元素数量增加了。

     java 代码

    1. public Object get(intindex) {   
    2.     //首先检查index是否合法...此处不显示这部分代码   
    3.   Entry e = header; //开始节点   
    4.   //向前或者向后查找,具体由哪一个方向距离较   
    5.   //近决定   
    6.   if (index < size/2) {   
    7.          for (int i = 0; i <= index; i++)   
    8.          e = e.next;   
    9.     } else {   
    10.          for (int i = size; i > index; i--)   
    11.          e = e.previous;   
    12.     }   
    13.     return e;   
    14. }    

            把元素插入列表很简单:找到指定索引的节点,然后紧靠该节点之前插入一个新节点

    java 代码

    1. public void add(int index, Object element) {   
    2.       //首先检查index是否合法...此处不显示这部分代码   
    3.    Entry e = header; //starting node   
    4.       //向前或者向后查找,具体由哪一个方向距离较近决定   
    5.    if (index < size/2) {   
    6.             for (int i = 0; i <= index; i++)   
    7.                   e = e.next;   
    8.       } else {   
    9.             for (int i = size; i > index; i--)   
    10.             e = e.previous;   
    11.       }   
    12.       Entry newEntry = new Entry(element, e, e.previous);   
    13.       newEntry.previous.next = newEntry;   
    14.       newEntry.next.previous = newEntry;   
    15.       size++;   
    16.    

            LinkedList通过一个双向链接的节点列表实现。要通过索引访问元素,你必须查找所有节点,直至找到目标节点。

            如果要从Java SDK得到一个线程安全的LinkedList,你可以利用一个同步封装器从Collections.synchronizedList(List)得到一个。然而,使用同步封装器相当于加入了一个间接层,它会带来昂贵的性能代价。和Vector相比,经过同步封装的LinkedList在性能上处于显著的劣势,因为Vector不需要为了线程安全而进行任何额外的间接调用。

            只有List和Map具有高效的线程安全实现(分别是Vector和Hashtable类)。这两个高效的线程安全类的存在只是为了向后兼容,而不是出于性能上的考虑。

            对于通过索引访问和更新元素,LinkedList实现的性能开销略大一点,因为访问任意一个索引都要求跨越多个节点。插入元素时除了有跨越多个节点的性能开销之外,还要有另外一个开销,即创建节点对象的开销。在优势方面,LinkedList实现的插入和删除操作没有其他开销,因此,插入-删除开销几乎完全依赖于插入-删除点离集合末尾的远近。

            ArrayList和Vector通常比LinkedList和同步封装之后的LinkedList有着更好的性能。有些情况下LinkedList会有更好的性能,例如,当大量元素需要同时加入到大型集合的开头和末尾时。一般而言,建议优先使用ArrayList/Vector类,只有当它们存在明显的性能问题而LinkedList能够改进性能时,才使用LinkedList。

    2、HashTable和HashMap的区别:

    1. HashTable的方法是同步的,HashMap未经同步,所以在多线程场合要手动同步HashMap这个区别就像Vector和ArrayList一样。
    2. HashTable不允许null值(key和value都不可以),HashMap允许null值(key和value都可以)。
    3. HashTable有一个contains(Object value),功能和containsValue(Object value)功能一样。
    4. HashTable使用Enumeration,HashMap使用Iterator。
    5. HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。

      Hashtable通过initial capacityload factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像getput这样的操作。

    6. 哈希值的使用不同

    HashTable直接使用对象的hashCode,代码是这样的:
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    而HashMap重新计算hash值,而且用与代替求模:
    int hash = hash(k);
    int i = indexFor(hash, table.length);

    static int hash(Object x) {
      int h = x.hashCode();

      h += ~(h << 9);
      h ^= (h >>> 14);
      h += (h << 4);
      h ^= (h >>> 10);
      return h;
    }
    static int indexFor(int h, int length) {
      return h & (length-1);
    }

    Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现。

    参考:

    http://www-128.ibm.com/developerworks/cn/java/l-collection/  集合与通用集合

    http://blog.csdn.net/daryl715/archive/2007/02/25/1513678.aspx Java 中Vector、ArrayList和LinkedList 的区别

    http://www.yesky.com/20030214/1652126.shtml Vector在Java编程中的应用

    http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/overview-summary.html sun java docs

    ArrayList的使用一

    http://hi.baidu.com/%BA%C3%D1%F2%BE%CD%BA%C3%D1%F9/blog/item/76ce44fa3f648d9559ee901b.html

  • 分享到:
    评论
    1 楼 hngmduyi 2013-07-01  
    支持一个。

    相关推荐

      JAVA中集合类的使用及解释

      JAVA中集合类的使用及解释

      JAVA中集合类一些常用类的总结

      本文将对Java中一些常用的集合类进行总结。 首先,ArrayList是List接口的一个实现,它允许我们在列表中按索引存取元素。在上述代码中,创建了一个ArrayList对象`list`并添加了不同类型的元素,包括字符串和自定义的...

      java自定义集合类

      自定义集合类则是开发者根据特定需求扩展Java集合框架的行为,以满足个性化或特定业务场景的功能需求。以下是对"java自定义集合类"这一主题的详细解释。 首先,Java集合框架包括接口(如List、Set、Map)和实现这些...

      java集合类线程安全.doc

      本文将结合上述 Bloch 关于线程安全等级的定义,对 Java 集合框架中的集合类进行线程安全性分析,并指出各个集合类在现实的编程环境中需要注意的并发编程的陷阱;同时对集合框架中通用算法对线程安全性的影响进行...

      java集合类详解(set list ArrayList等java集合类详述)

      Java 集合类是 Java 语言中的一种基本数据结构,用于存储和操作大量数据。集合类可以分为三大类:Collection、List 和 Set。 Collection 是集合框架中的根接口,提供了基本的集合操作,如 add、remove、contains 等...

      java基本集合类,java基本集合类

      Java集合框架是Java编程语言中的一个重要组成部分,它提供了多种数据结构,如列表、队列、集、映射等,方便程序员存储和管理对象。本篇文章将详细讲解Java中的基本集合类ArrayList、LinkedList和Vector,以及HashSet...

      java常用集合类总结

      Properties类是Java集合类中的一种特殊类,以键值对的形式存储数据,但只能存储字符串对。Properties类提供了两个方法:setProperties()和getProperties(),用于操作键值对。 在线程安全的集合类中,Vector、Stack...

      Java集合详解,详细讲解java的集合类

      本文将深入讲解Java集合类,特别是Collection接口和其下的List、Set,以及Map接口中的几个重要实现类。 首先,我们来看Collection接口。Collection是最基本的集合接口,它代表一组Object,即它的元素。Collection...

      Java集合排序及java集合类详解.pdf

      Java集合排序及java集合类详解.pdf

      Java 集合排序及java 集合类详解

      Java 集合排序及java 集合类详解 Java 集合排序及java 集合类详解,Java里面最重要、最常用也就是集合那部分了,能够用好集合和理解好集合对于做Java程序的开发拥有无比的好处。本教程详细解释了关于Java中的集合是...

      java的集合类教学

      总之,Java集合类提供了丰富的选择来满足不同场景下的数据存储需求,从无序不重复的Set到有序可重复的List,再到键值对的Map,都有对应的接口和实现类。理解并熟练掌握这些接口和实现类的特性和使用方法,是提高Java...

      java集合类详解

      Java集合类是Java语言中用来存储数据的结构,它们是Java开发中非常重要的组件。在Java 2平台之前,集合框架的组成较为零散,自Java 2平台的JDK 1.2版本之后,引入了集合框架(Collections Framework),为集合类提供...

      第13讲 JAVA集合类.ppt

      Java集合类是Java编程语言中用于存储和管理对象的关键组件,它们构成了Java Collections Framework的核心。这个框架提供了一组高效、灵活的数据结构,使得开发者能够轻松地处理数据集合,而无需关心底层实现的复杂性...

      java集合类演示源码

      集合类的框架为集合的实现者提供了大量的接口和抽象类,并对其中的某些机制给予了描述,例如,Iterator(迭代协议)。实现Comparable接口或Comparator接口,用户可以根据需要对集合中的元素进行排序。为了方便用户...

      Java集合类图片

      Java集合类,在图片上体现出来,为了更好的描述,本来是博客里的,不好往博客里插,所以单独弄出来了。

      java工具类集合

      Java工具类集合是Java开发中不可或缺的一部分,它们提供了一系列便捷的方法,帮助开发者高效地处理各种常见任务。在Java中,工具类通常被组织在各种包下,如`java.util`、`java.lang`、`java.io`等。下面将详细介绍...

      Java集合排序及java集合类详解

      在本篇中,我们将深入探讨Java集合的排序机制以及集合类的详细使用。 首先,我们来了解一下Java集合的基本分类。Java集合主要分为两大类:List(列表)和Set(集)。List是一个有序的集合,允许元素重复,并且可以...

    Global site tag (gtag.js) - Google Analytics