`
yde986
  • 浏览: 97864 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

集合 中Iterator 、Vector、ArrayList、List 使用深入剖析

阅读更多
     线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类。
Java代码 复制代码
Collection
├─List
│├─LinkedList
│├─ArrayList
│└─Vector
│ └Stack
└─Set
Map
├─Hashtable
├─HashMap
└─WeakHashMap

Collection接口
  Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些 Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类, Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
  所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的 Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
  如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
  
Java代码 复制代码
  Iterator it = collection.iterator(); // 获得一个迭代子
    while(it.hasNext()) {
      Object obj = it.next(); // 得到下一个元素
    }

  由Collection接口派生的两个接口是List和Set。 
      用Iterator模式实现遍历集合 
      Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。
例如,如果没有使用Iterator,遍历一个数组的方法是使用索引: 
Java代码 复制代码
   for(int i=0; i<array.size(); i++) { ... get(i) ... } 

      而访问一个链表(LinkedList)又必须使用while循环:
Java代码 复制代码
      while((e=e.next())!=null) { ... e.data() ... } 

      以上两种方法客户端都必须事先知道集合的内部结构,访问代码和集合本身是紧耦合,无法将访问逻辑从集合类和客户端代码中分离出来,每一种集合对应一种遍历方法,客户端代码无法复用。 
      更恐怖的是,如果以后需要把ArrayList更换为LinkedList,则原来的客户端代码必须全部重写。 
      为解决以上问题,Iterator模式总是用同一种逻辑来遍历集合: 
Java代码 复制代码
 for(Iterator it = c.iterater(); it.hasNext(); ) { ... } 

      奥秘在于客户端自身不维护遍历集合的"指针",所有的内部状态(如当前元素位置,是否有下一个元素)都由Iterator来维护,而这个Iterator由集合类通过工厂方法生成,因此,它知道如何遍历整个集合。 
      客户端从不直接和集合类打交道,它总是控制Iterator,向它发送"向前","向后","取当前元素"的命令,就可以间接遍历整个集合。 
      首先看看java.util.Iterator接口的定义:
Java代码 复制代码

  

  public interface Iterator {           boolean hasNext();           Object next();           void remove();       } 

      依赖前两个方法就能完成遍历,典型的代码如下:
Java代码 复制代码

 

      for(Iterator it = c.iterator(); it.hasNext(); ) {           Object o = it.next();           // 对o的操作...       } 

      在JDK1.5中,还对上面的代码在语法上作了简化: 
      // Type是具体的类型,如String。       for(Type t : c) {           // 对t的操作...       } 
      每一种集合类返回的Iterator具体类型可能不同,Array可能返回ArrayIterator,Set可能返回 SetIterator,Tree可能返回TreeIterator,但是它们都实现了Iterator接口,因此,客户端不关心到底是哪种 Iterator,它只需要获得这个Iterator接口即可,这就是面向对象的威力。 
      Iterator源码剖析 
      让我们来看看AbstracyList如何创建Iterator。首先AbstractList定义了一个内部类(inner class): 
Java代码 复制代码

 

    private class Itr implements Iterator {           ...       }

      而iterator()方法的定义是:
Java代码 复制代码

 

      public Iterator iterator() {           return new Itr();       } 

      因此客户端不知道它通过Iterator it = a.iterator();所获得的Iterator的真正类型。 
      现在我们关心的是这个申明为private的Itr类是如何实现遍历AbstractList的: 
Java代码 复制代码
   private class Itr implements Iterator {           int cursor = 0;           int lastRet = -1;           int expectedModCount = modCount;       } 

      Itr类依靠3个int变量(还有一个隐含的AbstractList的引用)来实现遍历,cursor是下一次next()调用时元素的位置,第一次调用next()将返回索引为0的元素。lastRet记录上一次游标所在位置,因此它总是比cursor少1。 
      变量cursor和集合的元素个数决定hasNext(): 
Java代码 复制代码

 

   public boolean hasNext() {           return cursor != size();       } 
      方法next()返回的是索引为cursor的元素,然后修改cursor和lastRet的值: 
Java代码 复制代码

  

  public Object next() {           checkForComodification();           try {               Object next = get(cursor);               lastRet = cursor++;               return next;           } catch(IndexOutOfBoundsException e) {               checkForComodification();               throw new NoSuchElementException();           }       } 

      expectedModCount表示期待的modCount值,用来判断在遍历过程中集合是否被修改过。AbstractList包含一个modCount变量,它的初始值是0,当集合每被修改一次时(调用add,remove等方法),modCount加1。因此,modCount如果不变,表示集合内容未被修改。 
      Itr初始化时用expectedModCount记录集合的modCount变量,此后在必要的地方它会检测modCount的值:
Java代码 复制代码

 

      final void checkForComodification() {           if (modCount != expectedModCount)               throw new ConcurrentModificationException();       } 

      如果modCount与一开始记录在expectedModeCount中的值不等,说明集合内容被修改过,此时会抛出ConcurrentModificationException。 
      这个ConcurrentModificationException是RuntimeException,不要在客户端捕获它。如果发生此异常,说明程序代码的编写有问题,应该仔细检查代码而不是在catch中忽略它。 
      但是调用Iterator自身的remove()方法删除当前元素是完全没有问题的,因为在这个方法中会自动同步expectedModCount和modCount的值: 
Java代码 复制代码
 public void remove() {           ...           AbstractList.this.remove(lastRet);           ...           // 在调用了集合的remove()方法之后重新设置了expectedModCount:           expectedModCount = modCount;           ...       } 

      要确保遍历过程顺利完成,必须保证遍历过程中不更改集合的内容(Iterator的remove()方法除外),因此,确保遍历可靠的原则是只在一个线程中使用这个集合,或者在多线程中对遍历代码进行同步。 
      最后给个完整的示例:      
Java代码 复制代码

 

Collection c = new ArrayList();       c.add("abc");       c.add("xyz");       for(Iterator it = c.iterator(); it.hasNext(); ) {           String s = (String)it.next();           System.out.println(s);       }

      如果你把第一行代码的ArrayList换成LinkedList或Vector,剩下的代码不用改动一行就能编译,而且功能不变,这就是针对抽象编程的原则:对具体类的依赖性最小。


List接口
  List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
和下面要提到的Set不同,List允许有相同的元素。
  除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个 ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
  实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。

LinkedList类
  LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的 get,remove,insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
  注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
    
Java代码 复制代码
List list = Collections.synchronizedList(new LinkedList(...));

ArrayList类
  ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
  每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
  和LinkedList一样,ArrayList也是非同步的(unsynchronized)。

Vector类
  Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和 ArrayList创建的 Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素)。
分享到:
评论

相关推荐

    实验05 Java集合.doc

    3)把集合中的元素打印出来(使用迭代器Iterator) 2、编写程序练习List集合的基本使用: 1) 创建一个只能容纳String对象名为names的ArrayList集合; 2)按顺序往集合中添加5个字符串对象:"张三"、"李四"、"王五...

    Collection 详细案例,集合的详细例子,list例子和详细解析,map详细例子和详细解析,set详细列子和详细解析,

    真个非常的全面,包含了全部集合的案例,有ArrayList,Iterator,vector,还有map集合,set集合,各种例子,注释非常的清晰,语言不罗嗦,格式非常清晰。个人制作

    Java 最常见的 208 道面试题:第二模块答案

    27. ArrayList 和 Vector 的区别是什么? 28. Array 和 ArrayList 有何区别? 29. 在 Queue 中 poll()和 remove()有什么区别? 30. 哪些集合类是线程安全的? 31. 迭代器 Iterator 是什么? 32. Iterator 怎么使用?...

    阿里P7面试题包含解答

    List中的元素有序、允许有重复的元素,Set中的元素无序、不允许有重复元素。 3. Vector线程同步,ArrayList、LinkedList线程不同步。 4. LinkedList适合指定位置插入、删除操作,不适合查找;ArrayList、Vector适合...

    Java集合容器面试题(2020最新版)

    文章目录集合容器概述什么是集合集合的特点集合和数组的区别使用集合框架的好处常用的集合类有哪些?List,Set,Map三者的区别?List、Set、Map 是否继承自 Collection 接口?List、Map、Set 三个接口存取元素时,各...

    超全Java集合框架讲解.md

    超全Java集合框架讲解 - 超全Java集合框架讲解 - 集合框架总览 - Iterator Iterable ListIterator - Map 和 Collection 接口 - Map 集合体系详解 - HashMap - LinkedHashMap - TreeMap - WeakHashMap - ...

    Java期末复习-类集框架

    List接口、ArrayList类、Vector类、栈操作类Stack、链表操作类LinkList、队列操作接口Queue、Set接口、HashSet类、TreeSet类、SortedSet接口 双值操作接口Map(key-&gt;value)及其子接口、子类: SortedMap接口、HashMap...

    java8集合源码分析-CollectionDemo:自己复习集合框架时候的例子

    内部使用的是一个HashMap集合key值当做我们存储的对象,value值是一个固定的Object对象 保证唯一性:元素hashCode和equals方法。hashCode方法相同,判断equals方法 ---LinkedHashSet: 有序,是HashSet的子类 2....

    Java 基础核心总结 +经典算法大全.rar

    -、Iterator Iterable ListIterator 二、Map 和 Collection 接口Map 集合体系详解 HashMap LinkedHashMap TreeMap WeakHashMap Hashtable Collection 集合体系详解 Set 接口 AbstractSet 抽象类SortedSet 接口...

    JDKAPI18CN(中文版)

    除了实现List 接口之外,该类还提供了一些方法来操纵内部使用的存储列表的数组的大小。 (这个类是大致相当于Vector,不同之处在于它是不同步的)。 该size,isEmpty,get,set,iterator和listIterator操作在固定...

    大数据面试题.pdf

    1-10)集合的扩充 ArrayList list = new ArrayList(90000); list扩充多少次?? public ArrayList() { this(10); } 默认的扩充是10由此计算 1-11)java的拆包与封包的问题 System.out.println("5" + 2); 52 1-12)...

    【JDK1.8源码剖析】Collection接口

    Collection接口是集合层次结构中的根接口。 (1)下面是常用集合类关系图 Collection  |___List 有序,可重复  |___ArrayList  |___LinkedList   |___Vector   |___Set 无序,不可重复  |___HashSet  |_...

    史上最全java面试,103项重点知识,带目录

    27. ArrayList 和 Vector 的区别是什么? 11 28. Array 和 ArrayList 有何区别? 12 29. 在 Queue 中 poll()和 remove()有什么区别? 12 30. 哪些集合类是线程安全的? 12 31. 迭代器 Iterator 是什么? 12 32. ...

    疯狂JAVA讲义

    7.4.2 ArrayList和Vector实现类 264 7.4.3 固定长度的List 266 7.5 Queue接口 266 7.5.1 LinkedList实现类 266 7.5.2 PriorityQueue实现类 269 7.6 Map 270 7.6.1 HashMap和Hashtable实现类 271 7.6.2 ...

    Java集合的这些知识,2020年了,你还不知道?那你就out了

    文章目录集合架构CollectionCollection方法迭代器—IteratorListListIteratorListIterator方法ArrayListArrayList遍历方式LinkedListLinkedList 遍历方式VectorArrayList 与 LinkedList 区别ArrayList 与...

    二十三种设计模式【PDF版】

    2.设计模式是比 J2EE 等框架软件更小的体系结构,J2EE 中许多具体程序都是应用设计模式来完成的,当你深入到 J2EE 的内 部代码研究时,这点尤其明显,因此,如果你不具备设计模式的基础知识(GoF 的设计模式),你很难...

    突破程序员基本功的16课.part2

    3.3.1 Vector和ArrayList的区别 3.3.2 ArrayList和LinkedList的实现差异 3.3.3 ArrayList和LinkedList的性能分析和适用场景 3.4 Iterator迭代器 迭代时删除指定元素 3.5 小结 第4课 Java的内存回收 4.1 Java...

Global site tag (gtag.js) - Google Analytics