Java 类集

摘要:Java中的类集实际上是动态对象数组,没有长度限制,可以任意扩充和丰富。从JDK1.5后,类集中添加了泛型声明,保证了类集使用的安全,所有的类集都位于java.util包下,类集有其继承结构,掌握好类集的使用对我们的程序开发有极大地帮助。

类集概述

Java中的类集,可分为单值操作集合,一对值的操作集合,输出集合。

其中Collection是最大的单值集合操作接口,Map是最大的一对值集合操作接口,Iterator是最大的输出接口。

Collection接口

观察以下Collection类型的定义:public interface Collection <E> extends Iterable <E>,说明其支持泛型操作。

Colletion接口包含以下操作方法。

方法 描述
boolean add(E e) 添加元素
boolean addAll(Collection<? extends E> c) 添加一组元素,子集合
void clear() 清空集合内容
boolean contains(Object o) 判断集合中是否包含对象o
boolean containsAll(Collection<?> c) 判断集合中是否包含子集合
boolean isEmpty() 判断集合是否为空
Iterator iterator() 为Iterator接口实例化对象,以访问该集合
boolean remove(Object o) 删除指定对象
boolean removeAll(Collection<?> c) 删除一组元素,子集合
int size() 返回集合大小,集合当前元素个数
Object[] toArray() 返回该集合的静态对象数组形式

在使用集合工具的时候,一般不直接使用Colletion接口,而多使用其子接口List和Set,虽然Collection接口还有两个子接口Queue和SortedSet但不常用。

其中List和Set最大区别就是List中允许重复元素,Set中不允许元素重复。

List接口

List接口对Collection接口有了一定的扩充,添加的方法如下:

方法 描述
void add(int index,E element) 在集合指定位置插入元素
boolean addAll(int index, Collection<? extends E> c) 在集合指定位置插入子集合
E get(int index) 取得索引位置集合内容
int indexOf(Object o) 返回指定对象在集合中的索引,-1为不存在
int lastIndexOf(Object o) 返回指定对象最后一次出现在集合中的位置
ListIterator listIterator() 判实例化ListIterator对象,便于List集合输出
E remove(int index) 删除指定索引位置的元素
E set(int index, E element) 修改指定索引位置的元素
List subList(int fromIndex, int toIndex) 截取子集合,从指定起始位置到结束位置

在使用List接口的时候,我们通常使用其子类ArrayList对其进行实例化,下面是ArrayList的使用示例:

import java.util.*;
public class TestCollection{
 	public static void main(String[] args) {
  		List<String> ls = new ArrayList<String>();
  		ls.add("hello");
  		ls.add("world");
  		ls.add("hello");
  		ls.add(0,"yes");
  		ls.remove("hello");
  		System.out.println(ls.contains("world"));
  		System.out.println(ls);
 	}	
}

程序输出:

true [yes, world, hello]

说明集合类已经覆写了toString方法。ArrayList类是JDK1.2引进的,List接口还有一个较老的子类Vector也可以对其进行实例化,主要操作方法addElement,另外可以返回枚举对其进行输出。

ArrayList和Vector主要区别在于:

LinkedList是扩展自List接口的链表操作类,使用方法与ArrayList类似,存储上有所差异,这里就不过多介绍了。

Set接口

Set接口也属于Collection的子接口,但它和List接口的最大区别在于,Set集合中不能包含重复元素,Set接口有两大子类,TreeSet和HashSet,其中TreeSet支持有序存放,HashSet属于散列存放。

Set接口并没有对Collection接口进行扩充。

首先使用HashSet对其进行实例化:

Set<String> st = new HashSet<String>();
st.add("A");
st.add("D");
st.add("C");//repeat element
st.add("C");
st.add("B");
st.add("B");//repeat element
st.add("E");
System.out.println(st);

输出:[D, E, A, B, C],从输出内容,可以发现Set不允许有重复元素,会自动屏蔽,HashSet对元素散列存放,没有任何顺序,将HashSet更改为TreeSet进行实例化得到输出结果如下:[A, B, C, D, E],说明,使用TreeSet对存放的元素进行顺序排放。

那么它是如何排序的呢,现在不存放String对象,改存放我们自定义的Person对象,其结果又如何呢?定义Person类如下:

class Person{
 	private String name = null;
 	private int age = 0;

 	public Person(){
 
 	}

 	public Person(String name, int age){
  		this.name = name;
  		this.age = age;
 	}

 	public String toString(){
  		return "name=" + this.name + " age=" + this.age;
 	}
}

在主函数定义Set集合,用于存放Person对象,

Set<Person> pr = new TreeSet<Person>();
pr.add(new Person("zhangsan",20));
pr.add(new Person("lisi",21));
pr.add(new Person("wangwu",15));
pr.add(new Person("zhaoliu",20)); 
System.out.println(pr);

主函数输出集合:

Exception in thread “main” java.lang.ClassCastException: Person cannot be cast to java.lang.Comparable at java.util.TreeMap.compare(Unknown Source) at java.util.TreeMap.put(Unknown Source) at java.util.TreeSet.add(Unknown Source) at TestCollection.main(TestCollection.java:27)

程序产生上述异常,可以发现,这时候Person类由于没有实现Comparable接口,因此不支持排序功能,而String默认是实现了Comparable接口的,现在修改Person类实现Comparable接口,覆盖compareTo方法,使其按年龄排序。

class Person implements Comparable<Person> {
  	private String name = null;
 	private int age = 0;

 	public Person(){
 
 	}

	public Person(String name, int age){
  		this.name = name;
  		this.age = age;
 	}

 	public int compareTo(Person per){
  		if(this.age < per.age){
   			return -1;
  		}else if(this.age > per.age){
   			return 1;
  		}else{
   			return 0;
  		}
 	}

 	public String toString(){
  		return "name=" + this.name + " age=" + this.age;
 	}
}

修改之后,正常输出并按年龄排序如下:

[name=wangwu age=15, name=zhangsan age=20, name=lisi age=21]

但是发现zhaoliu不见了,那是因为对年龄排序,由于zhangsan和zhaoliu的年龄一样,被当做重复元素处理掉了,现在修改compareTo方法

 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);
  	}
}

输出如下:

[name=wangwu age=15, name=zhangsan age=20, name=zhaoliu age=20, name=lisi age=21]

发现一切正常了,即使加上一个20岁的zhangsan,重复元素也会被剔除掉。

但这时将主函数中的TreeSet类改成HashSet类,输出:

[name=zhaoliu age=20, name=zhangsan age=20, name=zhangsan age=20, name=lisi age=21, name=wangwu age=15]

发现加上的重复元素zhangsan没有被剔除,是因为我们刚刚是对Person类借助于compareTo方法实现了比较剔除,而并没有从类本身考虑重复的情况,一旦比较不需要了,重复元素便不能判断出来。

那么Set集合又是如何排除重复元素的呢?答案是通过Obejct的equals和hashCode方法,来判断重复元素的,现在修改Person类如下:

class Person implements Comparable<Person> {
  	private String name = null;
 	private int age = 0;

 	public Person(){
 
 	}

 	public Person(String name, int age){
  		this.name = name;
  		this.age = age;
 	}

 	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 int hashCode(){
  		return this.name.hashCode()*this.age;
 	}

 	public boolean equals(Object o){
  		if(this == o)
   			return true;
  		if(!(o instanceof Person))
   			return false;
  		Person per = (Person)o;
  		if(this.name.equals(per.name) && this.age == per.age) 
   			return true;
  		else
   			return false;
 	}

 	public String toString(){
  		return "name=" + this.name + " age=" + this.age;
 	}
}

主函数再次进行调用:

Set<Person> pr = new TreeSet<Person>();
pr.add(new Person("zhangsan",20));
pr.add(new Person("lisi",21));
pr.add(new Person("wangwu",15));
pr.add(new Person("zhaoliu",20));
pr.add(new Person("zhangsan",20));
System.out.println(pr);

此时输出内容不会再出现重复元素了:

[name=wangwu age=15, name=zhangsan age=20, name=zhaoliu age=20, name=lisi age=21]

集合输出

集合的输出有四种方式:Iterator;ListIterator;foreach;Enumeration。

下面介绍这几种输出方法:

System.out.println("1.use iterator to print collection");
Iterator<Person> iter = pr.iterator();
while(iter.hasNext()){
	Person per = iter.next();
	System.out.println(per);
}

System.out.println("2.use listiterator to print List");
ListIterator<String> listIter = ls.listIterator();
System.out.println("2.1.forward print");
while(listIter.hasNext()){
	String str = listIter.next();
	System.out.println(str);
}
System.out.println("2.2.backward print");
while(listIter.hasPrevious()){
	String str = listIter.previous();
	System.out.println(str);
}

System.out.println("3.use general for to print List");
for(int i=0;i<ls.size();i++){
	String str = ls.get(i);
	System.out.println(str);
}

System.out.println("4.use foreach to print collection");
for(Person per : pr){
	System.out.println(per);
}

System.out.println("5.use enumeration to print Vector");
Enumeration<String> e = vec.elements();
while(e.hasMoreElements()){
	String str = e.nextElement();
	System.out.println(str);
}

注意,ListIterator对List集合进行双向输出时,必须先进行next输出之后,才能进行previous输出。至此已经介绍完了单一值接口和输出接口,还剩下一对值接口即Map接口。

Map 接口

Map作为一对值的最大接口,其值的存储是以键值对的形式存放的,同样我们也不直接操作Map接口,而是使用其子类进行实例化。

Map接口三个常用的子类是:HashMap,Hashtable以及TreeMap。

先来看一下Map接口定义的常用方法。Map接口定义:public interface Map<K,V>

常用方法:

方法 描述
V put(K key, V value) 添加键值对
V get(Object key) 根据指定键取值
boolean containsKey(Object key) 判断是否包含指定键
boolean containsValue(Object value) 判断是否包含指定值
Set keySet() 将所有的key转为Set集合
Collection values() 将所有的value转化为Collection集合
Set<Map.Entry<K,V» entrySet() 将Map转化为Set集合,此为Map输出方式
void clear() 清空Map集合
void size() 返回集合大小

以下代码使用HashMap为Map接口进行实例化,并调用相关方法

Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"hello");
map.put(2,"world");
map.put(3,"yes");
System.out.println(map);

Set<Integer> keys = map.keySet();
Iterator<Integer> keyIter = keys.iterator();
while(keyIter.hasNext()){
	Integer key = keyIter.next();
	String value = map.get(key);
	System.out.println(key+"---->"+value);
}

Collection<String> vals = map.values();
Iterator<String> valIter = vals.iterator();
while(valIter.hasNext()){
	String val = valIter.next();
	System.out.println(val);
}

运行结果:

{1=hello, 2=world, 3=yes} 1—->hello 2—->world 3—->yes hello world yes

HashMap本身也属于一种无序的操作。而Hashtable实际上与Vector产生的时代一样,属于最早集合操作类,之后只是扩展了其应用,实现了Map接口而已,,把上面HashMap声明部分,换成Hashtable,结果一模一样。

那么HashMap与Hashtale的区别与ArrayList和Vector区别一样:

从实际开发来看,最常用的子类是HashMap.注意Map接口由于以键值对的形式出现,无法直接使用Ierator接口进行输出。下面介绍一种Map接口常用输出方法。

Set<Map.Entry<Integer,String>> entrys = map.entrySet();
Iterator<Map.Entry<Integer,String>> entryIter = entrys.iterator();
while(entryIter.hasNext()){
	Map.Entry<Integer,String> me = entryIter.next();
	System.out.println(me.getKey()+"---->"+me.getValue());
}