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来对待。