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

2023-11-02

线性表,链表,哈希表是常用的数据结构在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类。 

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中每一个元素。典型的用法如下:
    Iterator it = collection.iterator(); // 获得一个迭代子
    while(it.hasNext()) {
      Object obj = it.next(); // 得到下一个元素
    }
  由Collection接口派生的两个接口是List和Set。

用Iterator模式实现遍历集合


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

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

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

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

依赖前两个方法就能完成遍历,典型的代码如下:
       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):
       private class Itr implements Iterator {
          ...
      }

而iterator()方法的定义是:
       public Iterator iterator() {
          return new Itr();
      }

因此客户端不知道它通过Iterator it = a.iterator();所获得的Iterator的真正类型。
现在我们关心的是这个申明为private的Itr类是如何实现遍历AbstractList的:
       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():
       public boolean hasNext() {
          return cursor != size();
      }

方法next()返回的是索引为cursor的元素,然后修改cursor和lastRet的值:
       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的值:
       final void checkForComodification() {
          if (modCount != expectedModCount)
              throw new ConcurrentModificationException();
      }

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

要确保遍历过程顺利完成,必须保证遍历过程中不更改集合的内容(Iterator的remove()方法除外),因此,确保遍历可靠的原则是只在一个线程中使用这个集合,或者在多线程中对遍历代码进行同步。
最后给个完整的示例:
       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:
    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的状态(例 如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该 异常。

Stack 类
  Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得 Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测 一个元素在堆栈中的位置。Stack刚创建后是空栈。

Set接口
  Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
  很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
  请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

Map接口
  请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个 value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。

Hashtable类
  Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。
  添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。
Hashtable 通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:
    Hashtable numbers = new Hashtable();
    numbers.put(“one”, new Integer(1));
    numbers.put(“two”, new Integer(2));
    numbers.put(“three”, new Integer(3));
  要取出一个数,比如2,用相应的key:
    Integer n = (Integer)numbers.get(“two”);
    System.out.println(“two = ” + n);
  由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方 法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相 同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如 果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希 表的操作。
  如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
  Hashtable是同步的。

HashMap类
  HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap 的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。

WeakHashMap类
  WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。

总结
  如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
  如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
  要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
  尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。

同步性
Vector 是同步的。这个类中的一些方法保证了Vector中的对象是线程安全的。而ArrayList则是异步的,因此ArrayList中的对象并不是线程安全 的。因为同步的要求会影响执行的效率,所以如果你不需要线程安全的集合那么使用ArrayList是一个很好的选择,这样可以避免由于同步带来的不必要的 性能开销。
数据增长
从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类型中增加元素的时候,如果元素的数目 超出了内部数组目前的长度它们都需要扩展内部数组的长度,Vector缺省情况下自动增长原来一倍的数组长度, ArrayList是原来的50%,所以最后你获得的这个集合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用 Vector有一些优势,因为你可以通过设置集合的初始化大小来避免不必要的资源开销。
使用模式
在ArrayList和Vector中,从一个指定的位置(通过索引)查找数据或是在集合的末尾增加、移除一个元素所花费的时间是一样的,这个时间我们用 O(1)表示。但是,如果在集合的其他位置增加或移除元素那么花费的时间会呈线形增长:O(n-i),其中n代表集合中元素的个数,i代表元素增加或移除 元素的索引位置。为什么会这样呢?以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素都要执行位移的操作。这一切意味着什么呢?
这意味着,你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或ArrayList都可以。如果是其他操作,你最好选择其他 的集合操作类。比如,LinkList集合类在增加或移除集合中任何位置的元素所花费的时间都是一样的?O(1),但它在索引一个元素的使用缺比较慢-O (i),其中i是索引的位置.使用 ArrayList也很容易,因为你可以简单的使用索引来代替创建iterator对象的操作。LinkList也会为每个插入的元素创建对象,所有你要 明白它也会带来额外的开销。
最后,在《Practical Java》一书中Peter Haggar建议使用一个简单的数组(Array)来代替Vector或ArrayList。尤其是对于执行效率要求高的程序更应如此。因为使用数组 (Array)避免了同步、额外的方法调用和不必要的重新分配空间的操作。















































































在使用 java的时候,我们都会遇到使用集合(collection)的时候,但是 java api提供了多种集合的实现。

总的说来, java api中所用的集合类,都是实现了collection接口,他的一个类继承结构如下:

                  collection<--list<--vector
                          collection<--list<--arraylist
                          collection<--list<--linkedlist
                          collection<--set<--hashset
                          collection<--set<--hashset<--linkedhashset
                          collection<--set<--sortedset<--treeset

vector : 基于array的list,其实就是封装了array所不具备的一些功能方便我们使用,它不可能走入array的限制。性能也就不可能超越array。所 以,在可能的情况下,我们要多运用array。另外很重要的一点就是vector&ldquo;sychronized&rdquo; 的,这个也是vector和arraylist的唯一的区别。

arraylist:同vector一样是一个基于array上的链表,但是不同的是arraylist不是同步的。所以在性能上要比vector优越一些,但是当运行到多 线程环境中时,可需要自己在管理 线程的同步问题。

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

list总结:

1. 所有的list中只能容纳单个不同类型的对象组成的表,而不是key-value键值对。例如:[ tom,1,c ];

2. 所有的list中可以有相同的元素,例如vector中可以有 [ tom,koo,too,koo ];

3. 所有的list中可以有null元素,例如[ tom,null,1 ];

4. 基于array的list(vector,arraylist)适合查询,而linkedlist(链表)适合添加,删除操作。

hashset: 虽然set同list都实现了collection接口,但是他们的实现方式却大不一样。list基本上都是以array为基础。但是set则是在 hashmap的基础上来实现的,这个就是set和list的根本区别。hashset的存储方式是把hashmap中的key作为set的对应存储项。 看看hashset的add(object  obj)方法的实现就可以一目了然了。

    public boolean add(object obj)
    {
        return map.put(obj, present) == null;
    }

这个也是为什么在set中不能像在list中一样有重复的项的根本原因,因为hashmap的key是不能有重复的。

linkedhashset:hashset的一个子类,一个链表。

treeset:sortedset的子类,它不同于hashset的根本就是treeset是有序的。它是通过sortedmap来实现的。

set总结:

1. set实现的基础是map(hashmap);

2.  set中的元素是不能重复的,如果使用add(object obj)方法添加已经存在的对象,则会覆盖前面的对象。
java.util包中包含了一系列重要的集合类。本文将从分析源码入手,深入研究一个集合类的内部结构,以及遍历集合的迭代模式的源码实现内幕。 

  下面我们先简单讨论一个根接口Collection,然后分析一个抽象类AbstractList和它的对应Iterator接口,并仔细研究迭代子模式的实现原理。 

  本文讨论的源代码版本是JDK 1.4.2,因为JDK 1.5在java.util中使用了很多泛型代码,为了简化问题,所以我们还是讨论1.4版本的代码。 

  集合类的根接口Collection 

  Collection接口是所有集合类的根类型。它的一个主要的接口方法是: 

  boolean add(Object c) 

  add()方法将添加一个新元素。注意这个方法会返回一个boolean,但是返回值不是表示添加成功与否。仔细阅读doc可以看到,Collection规定:如果一个集合拒绝添加这个元素,无论任何原因,都必须抛出异常。这个返回值表示的意义是add()方法执行后,集合的内容是否改变了(就是元素有无数量,位置等变化),这是由具体类实现的。即:如果方法出错,总会抛出异常;返回值仅仅表示该方法执行后这个Collection的内容有无变化。 

  类似的还有: 

  boolean addAll(Collection c); 
  boolean remove(Object o); 
  boolean removeAll(Collection c); 
  boolean remainAll(Collection c); 

  Object[] toArray()方法很简单,把集合转换成数组返回。Object[] toArray(Object[] a)方法就有点复杂了,首先,返回的Object[]仍然是把集合的所有元素变成的数组,但是类型和参数a的类型是相同的,比如执行: 

  String[] o = (String[])c.toArray(new String[0]); 

  得到的o实际类型是String[]。 

  其次,如果参数a的大小装不下集合的所有元素,返回的将是一个新的数组。如果参数a的大小能装下集合的所有元素,则返回的还是a,但a的内容用集合的元素来填充。尤其要注意的是,如果a的大小比集合元素的个数还多,a后面的部分全部被置为null。 

  最后一个最重要的方法是iterator(),返回一个Iterator(迭代子),用于遍历集合的所有元素。 

  用Iterator模式实现遍历集合 
   
  Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。 

  例如,如果没有使用Iterator,遍历一个数组的方法是使用索引: 

  for(int i=0; i<array.size();
 
  而访问一个链表(LinkedList)又必须使用while循环: 

  while((e=e.next())!=null) { ... e.data() ... } 

  以上两种方法客户端都必须事先知道集合的内部结构,访问代码和集合本身是紧耦合,无法将访问逻辑从集合类和客户端代码中分离出来,每一种集合对应一种遍历方法,客户端代码无法复用。 

  更恐怖的是,如果以后需要把ArrayList更换为LinkedList,则原来的客户端代码必须全部重写。 

  为解决以上问题,Iterator模式总是用同一种逻辑来遍历集合: 

  for(Iterator it = c.iterater(); it.hasNext(); ) { ... } 

  奥秘在于客户端自身不维护遍历集合的"指针",所有的内部状态(如当前元素位置,是否有下一个元素)都由Iterator来维护,而这个Iterator由集合类通过工厂方法生成,因此,它知道如何遍历整个集合。 

  客户端从不直接和集合类打交道,它总是控制Iterator,向它发送"向前","向后","取当前元素"的命令,就可以间接遍历整个集合。 

  首先看看java.util.Iterator接口的定义: 

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

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

  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): 

  private class Itr implements Iterator { 
  ... 
  } 

  而iterator()方法的定义是: 

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

  因此客户端不知道它通过Iterator it = a.iterator();所获得的Iterator的真正类型。 

  现在我们关心的是这个申明为private的Itr类是如何实现遍历AbstractList的: 

  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(): 

  public boolean hasNext() { 
  return cursor != size(); 
  } 

  方法next()返回的是索引为cursor的元素,然后修改cursor和lastRet的值: 

  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的值:

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

  如果modCount与一开始记录在expectedModeCount中的值不等,说明集合内容被修改过,此时会抛出ConcurrentModificationException。 

  这个ConcurrentModificationException是RuntimeException,不要在客户端捕获它。如果发生此异常,说明程序代码的编写有问题,应该仔细检查代码而不是在catch中忽略它。 

  但是调用Iterator自身的remove()方法删除当前元素是完全没有问题的,因为在这个方法中会自动同步expectedModCount和modCount的值: 

  public void remove() { 
  ... 
  AbstractList.this.remove(lastRet); 
  ... 
  // 在调用了集合的remove()方法之后重新设置了expectedModCount: 
  expectedModCount = modCount; 
  ... 
  } 

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

  最后给个完整的示例: 

  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,剩下的代码不用改动一行就能编译,而且功能不变,这就是针对抽象编程的原则:对具体类的依赖性最小。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 中Iterator 、Vector、ArrayList、List 使用深入剖析 的相关文章

  • Java Arraylist of object 按日期从数组列表中删除元素

    这是我的数组列表 ArrayList
  • 如何以编程方式检查应用程序是否在调试模式下运行?

    我必须在应用程序中的某个位置确定我的应用程序是在调试模式还是实时模式下运行 是否有任何函数或代码可用于检查 在开 关两种情况下都会返回 true false 如果是这样 请帮助我 提前致谢 从问题中尚不清楚调试模式是否指的是 应用程序是否可
  • 编译错误:Android Studio

    我正在尝试修改基于 IntelliJ 构建的现有 Android 项目 我已经搜索并尝试了很多东西 但我的错误仍然没有消失 Error 5 1 android apt compiler main D android tinynote app
  • 在 Java 中重置 Graphics2D 对象

    我正在用 Java 尝试 Graphics2D 但像往常一样 我被困住了 P 问题是 假设我有这个代码 Graphics2D g Graphics2D this getGraphics Inside a JFrame g rotate Ma
  • Windows 上的虚假唤醒。是否可以?

    我最近学习了 虚假唤醒 有人说这个问题只可能发生在某些类型的 Linux PC 上 我用的是窗户 我为虚假唤醒编写了测试 我得到的结果是这是可能的 但我想向您展示这个测试 也许我在某个地方犯了错误 我的初始变体 import java ut
  • 面试问题 - 在排序数组 X 中搜索索引 i,使得 X[i] = i

    昨天面试时 我被问到了以下问题 考虑一个 Java 或 C 数组X它已排序并且其中没有两个元素是相同的 如何最好地找到索引i这样该索引处的元素也是i 那是X i i 作为澄清 她还给了我一个例子 Array X 3 1 0 3 5 7 in
  • 定制法国号码格式

    我尝试为美国国家 地区使用自定义数字格式 到目前为止效果很好 Not something I want NumberFormat numberFormat0 NumberFormat getNumberInstance Locale US
  • 修复 java 内存泄漏的学习网站

    学习修复 java 内存泄漏的最佳地点是什么 我一直试图在网络上找到好的资源 但令我失望的是 我发现正在讨论玩具示例 我还能够对小型玩具转储进行故障排除 但现实世界的应用程序转储更具挑战性 并且提供的线索很少 我尝试过 Jhat JMap
  • std::vector 的复制构造函数如何运行?

    一个如何std vector
  • 仅使用 ServletContext 查找应用程序的 URL

    我正在使用 Spring MVC 编写一个 Java Web 应用程序 我有一个后台进程 它会遍历数据库并查找必须通过电子邮件发送给我的用户的通知 这些电子邮件需要包含应用程序的超链接 对于网络应用程序来说 这似乎是相当常见的模式 但我遇到
  • 用户“root”@“localhost”的访问被拒绝

    我正在尝试从数据库中获取记录 但我面临这个访问被拒绝的问题 我尝试了 Stack Overflow 上提到的其他解决方案 例如向用户授予权限 但没有任何效果 访问数据库的代码 public void service HttpServletR
  • 如何用java对jpg进行像素化?

    我正在尝试使用 Java 6 对 JPEG 进行像素化 但运气不佳 它需要使用 Java 而不是像 Photoshop 这样的图像处理程序 并且它需要看起来像老派 像这样 有谁能够帮助我 使用java awt image javadoc h
  • 使用 boost::iterator_facade<>

    我有一个链表结构 struct SomeLinkedList const char bar int lots of interesting stuff in here DWORD foo SomeLinkedList pNext 它是现有
  • 按位非运算符

    为什么要按位运算 0 打印 1 在二进制中 不是0应该是1 为什么 你实际上很接近 在二进制中 不是0应该是1 是的 当我们谈论一位时 这是绝对正确的 然而 一个int其值为0的实际上是32位全零 将所有 32 个 0 反转为 32 个 1
  • 用java解密AES加密文件

    我有一个使用 AES 使用 java 应用程序加密的文件 我还有一个加密的密钥文件 但我不明白如何使用密钥来解密文件 大多数教程和示例都会在一个地方创建临时随机密钥 加密文件和解密 所以 问题是如何指定解密时必须使用的密钥 EDIT 我发现
  • 如何迭代SparseArray?

    有没有办法迭代 Java SparseArray 适用于 Android 我用了sparsearray通过索引轻松获取值 我找不到 看来我找到了解决方案 我没有正确注意到keyAt index 功能 所以我会这样做 for int i 0
  • ObservableList 不更新 ArrayList

    对于学校作业 我们正在使用 JavaFX 中的 ObservableList 对象 对吗 我已经为此工作了一天多了 但无法弄清楚 老师只告诉我们 谷歌一下 所以这也没有帮助 基本上 我们正在开发一个基本的管理应用程序来跟踪人们及其家人 人们
  • 在调试模式下,哪些代码更改会自动反映在 Eclipse 中?

    我使用 eclipse 用于编写 调试 作为 IDE 在调试模式下 当我进行一些更改 例如初始化局部变量 时 它们会自动反映 但其他更改例如更改静态变量的值 有时我会收到一条消息 说我需要重新启动虚拟机 有时则不需要 现在的问题是哪些类型的
  • 仅在java中使用数组计算50的阶乘

    我是java的初学者 我有一个作业要编写一个完整的程序 使用数组计算 50 的阶乘 我无法使用像 biginteger 这样的任何方法 我只能使用数组 因为我的教授希望我们理解背后的逻辑 我猜 然而 他并没有真正教我们数组的细节 所以我在这
  • 跳过一行GridBagLayout

    我在 JFrame 上使用 GridBagLayout 我希望能够跳过一两行 但将这些行显示为空白 然后在这些行后面有一个按钮 我在文档中找不到任何方法来执行我所描述的操作 有谁知道我可以执行此操作的任何方法吗 发现它比添加空组件干净得多

随机推荐

  • Matplotlib控制线条的颜色与风格

    通常对图形的第一次调整是调整它线条的颜色与风格 plt plot 函数可以通过相应的参数设置颜色与风格 要修改颜色 就可以使用 color 参数 它支持各种颜色值的字符串 颜色的不同表示方法如下所示 plt plot x np sin x
  • 面试第一关:自我介绍【含自我介绍模板】

    芝士AI吃鱼 了解更多的面试 AI知识经验 自我介绍是每一个面试必不可少的环节 也是求职应聘必须克服的一关 通过自我介绍 吸引面试官的注意力 自我介绍是你与面试官建立联系的第一步 也是面试的一个重要环节 通过一个清晰 有条理的自我介绍 你可
  • 最详细查看apk签名信息

    首先你需要java环境 或者你安装了Android Studio 随便用手机下载一个apk 这里我用的是QQ的apk 然后将这个apk发送到电脑上 接下来将这个 apk后缀名改为 zip 如下图所示 然后就是超简单的解压缩 然后你可以看到下
  • 什么是数据安全性?

    数据安全性是指在数字信息的整个生命周期中保护数字信息不受未经授权的访问 损坏或盗窃 这个概念涵盖了信息安全的各个方面 从硬件和存储设备的物理安全到管理和访问控制 以及软件应用程序的逻辑安全 数据安全涉及部署工具和技术 以增强组织对其关键数据
  • rust react tauri app 现有前端项目打包(windows)

    现有前端项目打包 环境配置 nodejs及相应包管理器 npm或yarn rust 开发环境 WebView2 安装 下载地址https developer microsoft com en us microsoft edge webvie
  • 导入exel后端校验完直接返回结果excel流前端自动下载

    var formData new FormData layero find form 0 var xhr new XMLHttpRequest xhr responseType blob xhr onload function e if t
  • mysql 5.7 主从配置优化_mysql-5.7的主从配置

    mysql的主从配置 下载最新mysql 的yum源 1 wget https dev mysql com get mysql57 community release el6 11 noarch rpm 安装最新mysql rpm ivh
  • mysql binlog 日志详解

    一 binlog概述 binlog是Mysql sever层维护的一种二进制日志 与innodb引擎中的redo undolog是完全不同的日志 其主要是用来记录对mysql数据更新或潜在发生更新的SQL语句 并以 事务 的形式保存在磁盘中
  • SpringBoot对数据进行持久化

    SpringBoot关闭服务后 对数据进行持久化操作 文章目录 SpringBoot关闭服务后 对数据进行持久化操作 1 放入需要持久化的数据 2 调用自定义的销毁方法 3 关闭程序可见控制台输入需要持久化的数据 提示 以下是本篇文章正文内
  • textarea高度随内容自动改变

    需求 textarea默认的高度不是对着内容变化 而是随着内容增多 出现了滚动条 目前的需求是实现一个能够输入的textarea 并且高度跟着内容变化 发现了一个比较好用的插件flexText 但是这个基于jquery写的 目前的技术栈是r
  • 【DRF】序列化器ModelSerializer的使用

    不使用序列化器的过程 1 把前端发送请求过来的json字符串 通过json loads转换成字典 字典转换为Python对象 存在数据库 2 返回给前端数据 是把对象查询出来 转换成字典 再通过JsonResponse转换为json字符串
  • 通信技术及云计算

    绪论 传输网络层是物联网的重要基础设施 从通信的角度来讲 传输网作为通信网的一个业务网 通信子网 在通信网络中 它承载着 物联 的作用 物联网的数据传输 由早期采用的有线方式 到后期更多无线方式的使用 在两个或多个设备之间近距离的解决了物物
  • 853. 有边数限制的最短路 bellman_ford算法模板

    给定一个n个点m条边的有向图 图中可能存在重边和自环 边权可能为负数 请你求出从1号点到n号点的最多经过k条边的最短距离 如果无法从1号点走到n号点 输出impossible 注意 图中可能 存在负权回路 输入格式 第一行包含三个整数n m
  • 我们在囧途之程序员做私活小记

    注 本文使用第一人称 原型取材于周围同事或民间 不一定代表作者本人 欢迎大家提供您的囧途素材 发送至shenyisyn gmail com 讲起做私活 很多程序员都会或多或少的涉及过 一般咱要去接点私活可能会有以下几个原因 1 咱们 有长远
  • Win11运行cmd提示“请求的操作需要提升”的解决方法

    cmd是用于执行输入的命令 我们大部分都用其排除或解决某些类型的Windows问题 但是近期有部分Win11用户在运行cmd时出现提示 请求的操作需要提升 的情况 这是怎么回事呢 出现这一情况很有可能是因为操作权限不足导致的 还有详细的电脑
  • 游戏开发Unity UGUI知识系列:点击屏幕响应事件

    参考 https blog csdn net Ro969668074 article details 81362727 总结 核心是在monobehaviour的update方法对注册的方法进行调用
  • 使用Milvus2.0时pip install pymilvus后无法import pymilvus的问题

    原因 使用Milvus2 0版本时 对应的pymilvus版本已经是2 0 0rc5 或以上版本 了 不能使用pip install pymilvus安装低版本的pymilvus了 解决方案 pip install pymilvus 2 0
  • QGIS - 帮助文档汇总

    Ref 1 Welcome to the QGIS project 2 QGIS Tutorials and Tips QGIS Tutorials and Tips
  • &&&&&&用法

    的含义 按位与操作 按二进制位进行 与 运算 运算规则 有 0 则为 0 0 0 0 0 1 0 1 0 0 1 1 1 x的含义 x 在计算机存储是用x的补码存储 就是在x的值的基础上进行按位取反 x 之后在增加1所得 也就是说 x x
  • Java 中Iterator 、Vector、ArrayList、List 使用深入剖析

    线性表 链表 哈希表是常用的数据结构 在进行Java开发时 JDK已经为我们提供了一系列相应的类来实现基本的数据结构 这些类均在java util包中 本文试图通过简单的描述 向读者阐述各个类的作用以及如何正确使用这些类 Collectio