您的位置:首页 > 编程语言 > Java开发

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

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

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来对待。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: