Collection接口有两个子接口:List接口和Set接口。
List本身就是一个变长的数组,当数组中的空间用完后,就自动开辟一个更大的数据,然后将原来的数据复制到新数组中,以此来扩展数据的长度。ArrayList就是如此。List接口中的元素可以重复。
而Set接口是不允许存在重复元素的。
先看一个例子,现在写一个Point类:[code=java]package org.cxy.collection;
public class Point {
private int x;
private int y;
public Point(int x,int y){
this.x = x;
this.y = y;
}
public String toString(){
return "坐标(x="+this.x+",y="+this.y+")" ;
}
}[/code]然后写一个测试类:[code=java]package org.cxy.collection;
import java.util.*;
public class HashSetDemo {
public static void main(String[] args){
HashSet<Point> hash = new HashSet<Point>();
hash.add(new Point(5,4));
hash.add(new Point(5,4));
hash.add(new Point(1,2));
for(Point p:hash){
System.out.println(p);
}
}
}[/code]执行结果:[code=java]坐标(x=5,y=4)
坐标(x=5,y=4)
坐标(x=1,y=2)[/code]刚才说了,Set接口是可以消除重复元素的,但是程序的执行结果,却存在了相同的元素。这是为什么?
换成String类呢?[code=java]package org.cxy.collection;
import java.util.*;
public class HashSetDemo {
public static void main(String[] args){
HashSet<String> hash = new HashSet<String>();
hash.add("1");
hash.add("1");
hash.add("2");
for(String str:hash){
System.out.println(str);
}
}
}[/code]程序执行的结果:[code=java]2
1[/code]结果居然没有重复元素,这又是为什么?还有,为什么先加入的是“1”,而先输出的元素确实“2”?
咱们一个个的来解决。
如果您学习过《数据结构》这门课程,对Hash应该有点印象,对,hash表。他的特点就是高速存取,而且它是所有数据结构中存取元素速度最快的一种。而HashSet接口就是基于哈希表来设计的。
为什么Hash表最快?数据的存储首先就是需要查找出数据将要存取的位置,顺序、折半、索引表、二叉查找树的查找效率都与查找表(待查找元素组成的集合)的长度紧密相关。都需要使用关键字不断的和查找表中的元素进行匹配。而查找的终极、必杀的做法是不去或很少进行元素的匹配。散列查找就是通过散列函数来计算元素的位置,从而尽可能的减少匹配次数,以此来提高存取速度。
存取元素的位置,通过计算来得到。在Java中,这个计算的过程就由hashCode方法来完成。
当将元素A、B依次插入到HashSet中,输出数据的时候,A并不一定排在B的前面,因为元素存储位置是由元素的hash码来确定的。因此咱们也说,HashSet是无序存放元素的。
但是,凡是都有万一,万一插入数据的时候,计算出来的位置上,已经存在了元素,那又该怎么办?在《数据结构》中,存在了“冲突检测机制”,这个东西的目的,就是当目标位置存在元素的时候,就给待插入元素新找一个位置。
而在Java中,虽然Set接口,本身要求其内的元素不能重复,但是谁来保证元素不重复呢? 答案当然就是通过咱们自己重写hashCode()和equals()方法。
因此使用HashSet<E> 的add()方法插入元素的时候:
|- HashSet<E>会自动调用元素的hashCode()方法。
|- 然后根据hashCode()方法的返回值 来决定元素要插入的位置。
|- 如果该位置上已经存在元素了 则会调用该元素equals()方法进行比较。
|- 如果两个元素相等 则丢掉欲插入的元素。
|- 如果两个元素不相等 则新元素会被加入到另一个位置(通过冲突检测来决定哪一个位置),这样就消除了重复。
|- 范例1中使用的是Point类 其并没有重写这2个方法。因此无法消除重复。
|- 范例2中使用的是String类,在String类已经重写完了Object类的equals()和hashCode()方法,所以可以消除重复。
说白了:
|- 如果想完整的使用HashSet<E>类 那么最少要重写equals()和hashCode()方法。
|- 重写hashCode() 用于获得元素的存储位置。
|- 重写equals() 用于在两个元素的位置相同的时候 比较两个元素是否相等。
总结一下:
Set接口有两个子类:HashSet和TreeSet 。
|- HashSet
|- 特点:在不存在重复元素的基础上,还可以进行高速的存取元素。
|- 要求:需要为您的类重写hashCode()和equals()方法。
|- TreeSet
|- 特点:在不存在重复元素的基础上,还可以将元素自动排序。
|- 要求:需要为您的类实现Comparable接口,并重写compareTo方法。
|- 重写compareTo() 可以同时完成两份工作 排序和消除重复。
由于您没有问关于TreeSet的知识,所以我就不说了。
[ 本帖最后由 cxy_zy 于 2011-07-15 14:30 编辑 ] |