代码即财富之我学Java类集(8)

Java中的类集,可分为单值操作集合,一对值的操作集合,输出集合。其中Collection是最大的单值集合,Map是最大的一对值操作集合,Iterator是最大的输出集合。先来看Colletion集合:

观察以下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<E> 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接口对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<E> listIterator() 实例化ListIterator对象,便于List集合输出
E remove(int index)  删除指定索引位置的元素
E set(int index,  E element) 修改指定索引位置的元素
List<E> 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主要区别在于:时间上,ArrayList产生于JDK1.2,而Vector产生于JDK1.0;ArrayList是异步操作,因此性能较高,Vector属于同步操作,性能较低;ArrayList是非线程安全的操作类,Vector是线程安全的操作类;ArrayList当集合内容加之初始容量时,容量会自动扩充0.5倍,而Vector容量会自动扩充1倍。

LinkedList是扩展自List接口的链表操作类,使用方法与ArrayList类似,存储上有所差异,这里就不过多介绍了。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],从输出内容,可以发现,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类,发现加上的重复元素zhangsan没有被剔除,是因为我们刚刚是对Person类借助于compareTo方法实现了比较剔除,而并没有从类本身考虑重复的情况,一旦比较不需要了,重复元素便不能判断出来。

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

那么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。其中Iterator是推荐使用方式:只要碰到集合输出问题,就一定首先考虑使用Iterator接口进行输出。ListIterator只要List接口的子类对象才可以使用,可以进行双向输出。也可以使用一般的for循环输出List集合内容。foreach是JDK1.5版本新增的for循环增强的内容,不仅可以输出数组,也可以输出集合内容。Enumeration接口是一种古老的操作接口,只有Vector类elements方法可以返回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接口三个常用的子类是:HashMap,Hashtable以及TreeMap.先来看一下Map接口定义的常用方法。

Set< Map.Entry< K, V>> entrySet()
方法 描述
V put(K key, V value) 添加键值对
V get(Object key) 根据指定键取值
boolean containsKey(Object key)  判断是否包含指定键
boolean containsValue(Object value) 判断是否包含指定值
Set<K> keySet() 将所有的key转为Set集合
Collection<V> values() 将所有的value转化为Collection集合
Set<Map.Entry<K,V>> entrySet() 将Map转化为Set集合
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集合比较晚,异步处理,性能较高;Hashtable较早,同步处理,性能较差。从实际开发来看,最常用的子类是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());
  }

TreeMap是按Key排序的Map子类,TreeMap中的Key就是TreeSet,可以完全按TreeSet来对待。

你可能感兴趣的:(代码即财富之我学Java类集(8))