类集框架 一 :Collection接口:List集合、Set集合, 集合输出 - Java高级特性 15

目录

Collection接口

List集合接口

ArrayList子类

ArrayList保存自定义类

LinkedList子类

Vector子类

Set集合接口

HashSet子类

TreeSet子类

分析TreeSet类排序操作

分析重复元素删除

集合输出

Iterator输出

ListIterator输出

Enumeration枚举输出

foreach()方法


学习笔记

从JDK1.2开始Java引入了类集开发框架,所谓的类集指的就是一套动态对象数组的实现方案,在实际开发之中没有任何一项的开发可以离开数组,但是传统的数组实现起来非常的繁琐,而且长度是其致命伤,正是因为长度的问题数组不可能大范围的使用,但是开发不可能离开数据,所以最初就只能依靠一些数据结构来实现动态数组的处理,而且其中最为重要的两个结构就是:链表、树。但是面对这些数据结构的实现又不得不面对以下的一些困难:

  • 数据结构的代码实现困难;
  • 对于链表或者二叉树进行更新处理的时候的维护是非常麻烦的;
  • 对于链表或二叉树还要尽可能保证器操作的性能。

所以从JDK1.2开始Java引入了类集,类集的主要功能就是对常见的数据结构进行完整的包装,并且提供一系列的接口与实现子类来帮助用户减少数据结构所带来的开发困难。但是最初的类集实现对于数据的控制并不严格,全部采用了Object类型进行数据接收,而在JDK1.5之后由于泛型技术的推广,所以类集本身也得到了良好的改进,可以直接利用泛型来保存相同的数据,并且随着数据量不断的增加,从JDK1.8开始类集中的实现算法也得到了良好的性能提升。

同时在整个类集框架里面提供有如下的核心接口:Collection、List、Set、Map、Iterator、Enumeration、Queue、ListIterator。

Collection接口

java.util.Collection是单值集合操作最大的父接口,在该接口中定义有所有单值数据的处理,这个接口之中定义有如下的核心方法:

NO.方法名称类型描述
01public boolean add​(E e)普通向集合中保存数据
02public boolean addAll​(Collection<? extends E> c)普通追加一组数据
03 public void clear​()普通清空集合,让根节点为空,同时进行GC处理
04public boolean contains​(Object o)普通查询数据是否存在,需要equals()方法支持
05public boolean remove​(Object o)普通数据删除,需要equals()方法支持
06 public int size​()普通获取数据长度,不能草果整型的最大长度
07public Object[] toArray​()普通将集合变为对象数组返回
08public Iterator<E> iterator​()普通将集合变为Iterator返回

在进行集合操作处理的时候,有两个方法最为常用:【数据增加】add()、【数据输出】Iterator()。在JDK1.5以前Collection只是一个独立的接口,但是从JDK1.5之后提供有Iterable父接口,并且在JDK1.8之后针对Iterable也得到了扩充。从JDK1.5开始操作的一般都是Collection的两个子接口:允许重复的List子接口、不允许重复的Set子接口。  

List集合接口

List是Collection的子接口,其最大的特点是允许保存有重复元素的数据,该接口的定义如下:

  • public interface List<E> extends Collection<E>

但是要清楚的是List子接口对于Collection接口进行扩充:

NO.方法名称类型描述
01public E get​(int index)普通获取指定索引上的数据
02 public E set​(int index,E element)普通修改指定索引数据
03public ListIterator<E> listIterator​()普通返回ListIterator的接口对象

但是List本身依然属于一个接口,那么对于接口本身要想使用则一定要使用子类来完成定义,在List子接口中有三个常用子类:ArrayList、Vector、LinkdList。

从JDK1.9开始List子接口里面追加有一些static方法,以方便用户的处理。

范例:观察List中的static方法

package cn.ren.demo;

import java.util.List;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		List<String> all = List.of("hello", "World", "你好", "世界", "饿") ;
		Object result []  = all.toArray() ; 
		for (Object temp : result) {
			System.out.println(temp + "、");
		}
		System.out.println(all);
		
	}	
}

这些操作是新版本之后添加的新功能。

ArrayList子类

ArrayList是List子接口使用最多的子类,但是这个子类使用的时候有前提要求的,下面对其相关定义和源代码组成进行分析,在Java中ArrayList的定义如下:

public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable

这个ArrayList的继承结构如下所示:

范例:使用ArrayList实例化List父接口

package cn.ren.demo;

import java.util.ArrayList;
import java.util.List;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		List<String> all = new ArrayList<String>() ;  // 为父接口进行实例化
		all.add("hello") ;
		all.add("hello") ;   // 代码中有重复数据
		all.add("ren") ; 
		System.out.println(all);
	}	
}

通过本程序可以发现List存储的特征:

  • 保存的顺序就是其存储的顺序;
  • List集合里面允许存在有重复数据;

在以上的程序里面虽然实现了程序输出,但是这种输出操作是直接利用了每个类提供的toString方法实现的,为了方便输出处理,在JDK1.8之后Iterable父接口之中定义有一个foreach()方法,方法定义如下:

  • 输出支持:default void forEach​(Consumer<? super T> action)

范例:利用forEach()方法输出(不是标准输出)

package cn.ren.demo;

import java.util.ArrayList;
import java.util.List;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		List<String> all = new ArrayList<String>() ;  // 为父接口进行实例化
		all.add("hello") ;
		all.add("hello") ;   // 代码中有重复数据
		all.add("ren") ; 
		all.forEach((str)->{
			System.out.print(str + "、");
		});
	}	
}

需要注意的是这种输出并不是在正常开发形式下考虑的操作形式。

范例:观察List集合的其它操作方法

 

package cn.ren.demo;

import java.util.ArrayList;
import java.util.List;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		List<String> all = new ArrayList<String>() ;  // 为父接口进行实例化
		System.out.println("集合是否为空?" + all.isEmpty() + "、集合元素个数:" + all.size());
		all.add("hello") ;
		all.add("hello") ;   // 代码中有重复数据
		all.add("ren") ;
		all.add("57") ;
		all.remove(2) ;
		all.remove("hello") ; // 删除元素是顺序删除的
		System.out.println("集合是否为空?" + all.isEmpty() + "、集合元素个数:" + all.size());
		all.forEach((str)->{
			System.out.print(str + "、");
		});
	}	
}

如果以方法的功能为例,那么ArrayList里面的操作支持与之前编写链表的形式是非常相似的,但是它并不是使用链表来实现的。通过类名称实际上就可以清楚的发现,ArrayList应该封装的是一个数组。

ArrayList构造:

public ArrayList​()

 

ArrayList构造:public ArrayList​(int initialCapacity)

 

通过有参的构造方法可以发现,在ArrayList里面所包含的数据实际上就是一个对象数组。如果现在在进行数据追加的时候ArrayList集合里面保存的对象数组的长度不够的时候那么会进行新的数组开辟,同时将旧数组中的内容拷贝到新数组之中,而后数组的开辟操作。

其中newCapacity中,第二个if条件只有空数组增加第一个元素时,才会进入;

如果在实例化ArrayList类对象的时候并没有传递初始的长度,则默认情况下会使用一个空数组,但是如果在进行数据增加的时候发现数组的容量不够了,则会判断当前增长的容量与默认容量的大小,使用较大的一个数值进行数组的开辟,所以可以得出一个结论;

  • JDK1.9之后:ArrayList默认的构造只会使用默认的空数组,使用的时候才会开辟数组,默认开辟的长度为10;
  • JDK1.9之前:ArrayList默认的构造实际上就会默认开辟大小为10的数组。

即:以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为10。

当ArrayList之中保存的容量不足的时候会采用成倍的方式进行增长,原始长度为10,下次的增长就是20,依次类推。在使用ArrayList子类的时候,一定要估算出你的数据量,如果超过10个使用有参构造方法进行创建,以避免垃圾空间的产生。

ArrayList保存自定义类

通过之前的分析已经清楚了ArrayList的子类的实现原理以及List的核心操作,但是在测试的时候使用的是系统提供的String类这是一个设计非常完善那个的类,而对于类集而言也可以实现自定义类对象的保存。

范例:实现自定义类对象的保存

package cn.ren.demo;

import java.util.ArrayList;
import java.util.List;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		List<Person> all = new ArrayList<Person>() ;
		all.add(new Person("张三", 20));
		all.add(new Person("李四", 25));
		all.add(new Person("王五", 10));
		all.add(new Person("赵六", 100));
		all.forEach(System.out::println);   // 方法引用代替消费型的接口
		all.remove(new Person("李四", 25)) ;
		System.out.println(all.contains(new Person("赵六", 100)));
		all.forEach(System.out::println);   // 方法引用代替消费型的接口
	}	
}



class Person {
	private String name ;
	int age ;
	public Person(String name, int age) {
		this.name = name;
		this.age = age ;
	}
	 // setter 、gettter 构造略
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true ;
		}
		if (obj == null) {
			return false ;
		}
		if (!(obj instanceof Person)) {
			return false ;
		}
		
		Person per = (Person) obj ;
		return this.name.equals(per.name ) && per.age == this.age ;
	}
	public String toString() {
		return "姓名:" + this.name + "、年龄:" + this.age ;
	}
}

在使用List保存自定义类对象的时候如果需要用到contains()、remove()方法进行查询和删除的时候,一定要保证类之中一定覆写了equals()方法。

LinkedList子类

在List接口里面还有另外一个比较常用的子类:LinkedList,这个类通过名称就可以发现:基于链表的实现。下面来观察其定义:

public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable

范例:使用LinkedList使用集合操作

package cn.ren.demo;

import java.util.LinkedList;
import java.util.List;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		List<String> all = new LinkedList<String>() ;  // 为父接口进行实例化
		all.add("hello") ;
		all.add("hello") ;   // 代码中有重复数据
		all.add("ren") ;
		all.add("57") ;
		all.forEach(System.out::println);
	}	
}

如果现在只是观察程序的功能会发现和ArrayList是完全一样的,但是会发现其内部实现机制是完全不同的。LinkedList的构造方法里面并没有像ArrayList那样的初始化大小的方法,而只是提供无参的构造处理方法“public LinkedList​()”。随后观察add()方法的具体实现。

    public boolean add(E e) {
        linkLast(e);
        return true;
    }

在之前编写自定义链表是判断了传入数据是否为null,如果为null则不进行保存,但是在LinkList并没有做这样的处理,而是LinkList里面并没有做这样的处理,而是所有的数据都可以保存,而后此方法调用了LinkLast()(在最后一个节点之后追加)方法。

    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

在Linked类里面保存的数据都是利用Node节点进行封装处理,同时为了提高程序执行的性能每一次都会保存上一个追加的节点(最后一个节点)这样就可以在增加数据的时候避免递归处理,在增加数据的时候要进行数据长度的增加。

通过以上分析LinkedList保存的就是链表的实现。

面试题:ArrayList与LinkedList的区别?

  • ArrayList是数组实现的集合操作,而LinkedList是链表实现的集合操作。
  • 在世List集合中的get()方法根据索引获取数据时,ArrayList时间复杂度时“O(1)”,而LinkedList得时间复杂度是"O(n)";
  • ArrayList在使用得时候默认得初始化对象数组的大小长度为10,如果空间不足则会采用2倍数的形式增长,如果保存大数据量的时候有可能会造成垃圾的产生使用性能的下降,这个时候可以使用LinkedList子类保存。

Vector子类

Vector是一个原始古老的程序类,这个类是在JDK1.0的时候就提供的,而后到了JDK1.2的时候由于开发者已经习惯使用Vector了,并且许多的系统类也是基于Vector实现的,考虑到其使用的广泛性,所以类集框架将其保存了下来,并且让其多实现了一个List的接口,观察Vector定义结构:

public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable

继承结构和ArrayList是相同的,所以这个类继承结构如下:

范例:Vector使用

package cn.ren.demo;

import java.util.List;
import java.util.Vector;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		List<String> all = new Vector<String>() ;  // 为父接口进行实例化
		all.add("hello") ;
		all.add("hello") ;   // 代码中有重复数据
		all.add("ren") ;
		all.add("57") ;
		all.add(null) ;
		all.forEach(System.out::println);
	}	
}

下面进一步观察Vector类的实现:

    public Vector() {
        this(10);
    }
    public Vector(int initialCapacity) {
        this(initialCapacity, 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;
    }

Vector类如果使用的是无参构造方法,则一定会默认开辟一个长度为10的数组,而后其余的实现操做与ArrayList是相同的。通过源代码分析可以发现Vector来之中的操作方法都是采用synchronized同步处理,而ArrayList并没有进行同步处理,所以ArrayList的方法在多线程访问时候是属于线程安全的,但是性能不如ArrayList高。

Set集合接口

Set集合最大的特点是不允许保存重复的元素,其也是Collection子接口。

在JDK1.9以前Set集合与Collection集合的定义并无差别,Set继续使用Collection接口中提供的方法进行操作,但是从JDK1.9之后,Set集合也像List集合一样扩充了一些static方法,Set集合定义如下:

public interface Set<E>
extends Collection<E>

需要注意的是,Set集合并没有像List集合那样扩充了许多新的方法,所以无法使用List集合中提供的get()方法,也就是说无法实现指定索引数据的获取。

Set集合继承关系如下:

从JDK1.9之后,Set集合也提供像List集合之类似的of()的静态方法。下面就是用此方法进行Set集合特点的验证。

范例:验证Set集合特征

package cn.ren.demo;

import java.util.Set;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		// 进行Set数据的保存,保存有重复元素
		Set<String> all = Set.of("hello", "hello", "ren", "57") ;
		all.forEach(System.out::println);
	}	
}

当使用of()方法的时候,如果发现重复元素则会直接抛出异常。这与传统set集合不保存重复元素的特点相一致,只不过自己抛出异常而已

Set集合的常规使用形式一定是依靠子类进行实例化的,所以Set接口之中有两个常用的子了一个是HashSet、TreeSet。

HashSet子类

HashSet是Set接口之中使用最多的一个子类,其最大特点是保存的数据是无序的,HashSet子类的继承关系如下:

public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, Serializable

这种继承的形式和ArrayList是非常相似的,

范例:观察HashSet子类

package cn.ren.demo;

import java.util.HashSet;
import java.util.Set;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		// 进行Set数据的保存,保存有重复元素
		Set<String> all =  new HashSet<String>() ;
		all.add("hello") ;
		all.add("nihao") ;
		all.add("hello") ; // 重复元素
		all.add("ren") ;
		all.add("57") ;
		all.add("World") ;
		all.forEach(System.out::println);
	}	
}

通过执行可以发现HashSet子类的特点:不允许保存重复的元素(Set接口定义的);HashSet保存的数据是无序的。

TreeSet子类

Set接口的另外一个子类就是TreeSet,与HashSet最大的一个区别在于TreeSet集合里面所保存的数据是有序的。首先观察一下TreeSet的定义:

public class TreeSet<E>
extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, Serializable

在这个子类之中依然继承AbstractSet父抽象类,同时有实现了一个NavigableSet的父接口。

范例:使用TreeSet子类:

package cn.ren.demo;

import java.util.Set;
import java.util.TreeSet;

public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		// 进行Set数据的保存,保存有重复元素
		Set<String> all =  new TreeSet<String>() ;
		all.add("hello") ;
		all.add("nihao") ;
		all.add("hello") ; // 重复元素
		all.add("ren") ;
		all.add("57") ;
		all.add("World") ;
		all.forEach(System.out::println);
	}	
}

当利用TreeSet保存的数据时候所有的数据都将按照数据的升序进行自动的排序处理。

分析TreeSet类排序操作

经过分析之后发现TreeSet子类之中保存的数据是允许排序的,但是需要注意的是这个类必须继承Comparable的父接口,因为只有实现了此接口才能够确认出对象的大小关系。

  • TreeSet本质上是利用TreeMap子类实现的集合数据的存储,而TreeMap(树)就需要根据Comaprable来确定大小关系。

那么使用一个自定义的类来实现排序的处理操作

范例:自定义类操作

package cn.ren.demo;

import java.util.Set;
import java.util.TreeSet;
class Person implements Comparable<Person>{
	private String name ;
	private int age ;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [姓名:" + this.name + ", 年龄:" + this.age + "]";
	}
	@Override
	public int compareTo(Person per) {
		if (this.age < per.age ) {
			return -1 ;
		} else if (this.age > per.age ) {
			return 1 ;
		} else {
			return this.name.compareTo(per.name ) ;
		}
	}
}
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		// 进行Set数据的保存,保存有重复元素
		Set<Person> all =  new TreeSet<Person>() ;
		all.add(new Person("张三", 10)) ;
		all.add(new Person("李四", 99)) ;
		all.add(new Person("王五", 50)) ;
		all.add(new Person("王五", 50)) ;   // 数据重复
		all.add(new Person("赵六", 70)) ;
		all.add(new Person("孙七", 70)) ;   // 年龄相同,姓名不同
		all.forEach(System.out::println);
	}	
}

在进行自定义类进行比较的时候一定要将该类之中的所有属性的依次进行大小关系的匹配,否则某一个或某几个属性相同的时候他也会认为是重复数据,所以TreeSet是利用了Comparable接口来确认重复数据的。

由于TreeSet在操作过程之中需要将类中的所有的属性进行比对,这样的实现太繁琐,因此在实际之中应该首选HashSet子类进行存储。

分析重复元素删除

TreeSet子类是利用了Comparable的接口来实现了重复元素的判断,但是Set集合的整体特征就是不允许保存重复元素,但是HashSet判断重复元素并不是利用Comparable完成的,利用的是Object类中提供的方法实现的。

  • 对象编码:public int hashCode​()
  • 对象比较:public boolean equals​(Object obj)

在进行重复元素判断的时候首先利用HashCode()进行编码的匹配,如果编码不存在则表示数据不存在,证明没有重复,如果编码存在了则进一步对象比较处理,如果发现重复了,则此数据不允许保存的。如果使用的是Eclipse开发工具则可以帮助开发者自动创建hashCode()和equals()方法。

 

范例:实现重复元素处理

package cn.ren.demo;

import java.util.HashSet;
import java.util.Set;
class Person implements Comparable<Person>{
	private String name ;
	private int age ;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [姓名:" + this.name + ", 年龄:" + this.age + "]";
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
}
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		// 进行Set数据的保存,保存有重复元素
		Set<Person> all =  new HashSet<Person>() ;
		all.add(new Person("张三", 10)) ;
		all.add(new Person("李四", 99)) ;
		all.add(new Person("王五", 50)) ;
		all.add(new Person("王五", 50)) ;   // 数据重复
		all.add(new Person("赵六", 70)) ;
		all.add(new Person("孙七", 70)) ;   // 年龄相同,姓名不同
		all.forEach(System.out::println);
	}	
}

在Java之中进行重复元素的判断处理利用的就是hashCode()与equals()两个方法作用完成,只有在排序要求的情况下(TreeSet)才会利用Comparable接口来实现。

集合输出

集合输出实际上从JDK1.8开始就在Iterable接口之中提供一个forEach()方法,但是这种方法输出并不是传统意义上的集合的输出形式,并且也很难在实际的开发之中出现,对于集合操作而言,一共定有四种输出形式:Iterator迭代输出(95%)、ListIterator双向迭代输出(0.1%)、Enumeration枚举输出(4.8%)、foreach输出(等同于Iterator)。

Iterator输出

通过Collection接口继承关系可以发现,从JDK1.5开始其多继承了一个Iterable父接口,并且在这个接口里面定义有iterator()操作方法,通过此方法可以获取Iterator接口对象(在JDK1.5之前这一方法直接定义在Collection)。

  • 获取Iterator接口对象:public Iterator<T> iterator​() ;

在Iterator接口里面定义有如下的方法:

NO.方法名称类型描述
01public boolean hasNext​()普通判断是否有数据
02 public E next​()普通取出当前数据
03public default void remove​()普通删除

在之前使用的java.util.Scanner类就是Iterator接口的子类,所以此类继承关系如下:

范例:使用Itrator输出

package cn.ren.demo;

import java.util.Iterator;
import java.util.Set;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Set<String> all = Set.of("hello", "world", "ren", "57") ;
		Iterator<String> iter = all.iterator() ; 	// 实例化Iterator对象
		while (iter.hasNext()) {
			System.out.println(iter.next());
		}
	}	
}

但是对于Iterator接口中remove()方法的使用需要特别注意(如果不是必须不要使用)。实际上在Collection中定义有数据删除的操作方法,但是进行迭代输出过程如果使用了Collection中的remove()方法会导致迭代失败。

范例:采用Collection集合中的remove()方法删除

package cn.ren.demo;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Set<String> all = new HashSet<String>() ;
		all.add("hello") ;
		all.add("world") ;
		all.add("ren" ) ;
		all.add("57") ;
		Iterator<String> iter = all.iterator() ; 	// 实例化Iterator对象
		while (iter.hasNext()) {
			String str = iter.next() ;
			if ("world".equals(str)) {
				all.remove("world") ;
			}
			System.out.println(str);
		}
	}	
}

此时无法进行数据的删除操作,所以只能使用Iterator接口中的remove()方法删除。

范例:使用Iterator接口之中的删除方法

package cn.ren.demo;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Set<String> all = new HashSet<String>() ;
		all.add("hello") ;
		all.add("world") ;
		all.add("ren" ) ;
		all.add("57") ;
		Iterator<String> iter = all.iterator() ; 	// 实例化Iterator对象
		while (iter.hasNext()) {
			String str = iter.next() ;
			if ("world".equals(str)) {
				iter.remove() ;   // 删除当前元素
			}
			System.out.println(str);
		}
		System.out.println("*****" + all );
	}	
}

此时程序执行之后没有出现任何的错误,并且可以成功的删除原始集合中的数据。

面试题:Collection.remove()与Iterator.remove()的区别?

  • 在进行迭代输出的时候如果使用了Collection.remove()则会造成并发更新的异常,导致程序删除出错;此时只能够应用Iterato.remove()方法实现正常的删除处理。

ListIterator输出

使用Iterator进行的迭代输出操作有一个特点:只允许由前向后实现输出,而如果说你现在需要进行双向迭代处理,那么就必须依靠Itrator的子接口:ListIterator接口来实现。需要注意的是如果要想获取ListIterator接口对象Collection并没有定义相关的处理方法,但是List子接口有,也就是说这个输出的接口是专门为List集合准备的。

在ListIterator接口里面定义有如下的操作方法:

  • 判断是否有前一个元素:public boolean hasPrevious​() ;

  • 获取当前元素:public E previous​();

范例:实现双向迭代

package cn.ren.demo;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		List<String> all = new ArrayList<String>() ;
		all.add("hello") ;
		all.add("world") ;
		all.add("ren" ) ;
		all.add("57") ;
		ListIterator<String> iter = all.listIterator() ; 	
		System.out.println("由前向后输出");
		while (iter.hasNext()) {
			String str = iter.next() ;
			System.out.print(str + "、");
		}
		System.out.println("\n由后向前输出:");
		while (iter.hasPrevious()) {
			System.out.print(iter.previous() + '、');
		}
	}	
}

如果要想实现由后向前的遍历,那么首先要实现由前向后实现遍历处理。

Enumeration枚举输出

Enumeration是在JDK1.0的时候就使用的输出接口,这个输出接口主要是为了Vector类提供服务的,也就是说到后续的一个发展Enumeration依然只为Vector一个类服务,如果要想获取Enumeration接口的对象就必须依靠Vector类提供的方法。

在Enumeration接口之中定义有两个操作方法:

  • 判断是否有下一个元素:public boolean hasMoreElements​()
  • 获取当前元素:public E nextElement​()

范例:使用Enumeration实现输出

package cn.ren.demo;

import java.util.Enumeration;
import java.util.Vector;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		Vector<String> all = new Vector<String>() ;
		all.add("hello") ;
		all.add("world") ;
		all.add("ren" ) ;
		all.add("57") ;
		Enumeration<String> enu = all.elements() ;
		while (enu.hasMoreElements()) {
			String str = enu.nextElement() ;
			System.out.print(str + "、");
		}
		
	}	
}

由于该接口出现的时间比较长了,所以在一些比较早的开发过程之中,也有部分的方法支持Enumration输出操作,但是随着类方法的不断完善,大部分的操作都可以直接利用Iterator实现了。

foreach()方法

除了使用迭代接口实现输出之外,从JDK1.5开始加强型的for循环(之前在数组中使用)也可以实现集合的输出操作。这个输出与数组操作形式类似。

范例:使用foreach输出

package cn.ren.demo;

import java.util.ArrayList;
import java.util.List;
public class JavaAPIDemo {
	public static void main(String[] args) throws Exception {
		List<String> all = new ArrayList<String>() ;
		all.add("hello") ;
		all.add("world") ;
		all.add("ren" ) ;
		all.add("57") ;
		for (String str : all) {
			System.out.print(str + '、');
		}
		
	}	
}

这种输出最初出现的时候并不建议使用,因为标准的集合操作还是应该以Iterator为主,但是JDK1.5已经推出很多年了,很多语法也开始被大部分人所习惯。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值