Java Set接口用法详解
Set 是 Collection 的子接口,Set 接口以散列的形式存储数据,所以元素没有顺序,它可以存储一组无序且唯一的对象。
Set 接口的定义为:
HashSet 的定义为:
LinkedHashSet 是 Set 的另外一个子接口,可以存储一组有序且唯一的元素。这里的有序是指元素的存储顺序和遍历顺序是一致的。
LinkedHashSet 的定义为:
我们对集合执行了添加两个“Hello”的操作,但是只保存了一个。这是因为 LinkedHashSet 集合的元素是唯一的,即不能出现两个相等的元素。字符串如此,其他对象也是一样的。我们定义一个 A 类,将类的实例化对象存入集合,代码为:
现在有一个需求,只要两个对象的 num 值相等,就认为是同一个对象,那如何修改代码呢?说明一下用 LinkedHashSet 判断两个对象是否相等的原理,首先会判断两个对象的 hashCode 是否相等,什么是hashCode?简单来说就是将对象的内部信息(如内存地址、属性值等),通过某种特定规则转换成一个散列值,也就是该对象的 hashCode。两个不同对象的 hashCode 可能相等,但是 hashCode 不相等的两个对象一定不是同一个对象。
所以集合在判断两个对象是否相等时,会先比较它们的 hashCode,如果不相等,则认为不是同一个对象,可以添加。如果 hashCode 相等,还不能认为两个对象就是相等的,需要通过 equals() 方法进一步判断。如果 equals() 方法为 true,则不会重复添加;如果 equals() 方法为 false,则正常添加。先判断 hashCode 是否相等可以减少 equals() 方法的调用,提高效率。所以两个 A 相等的前提是 hashCode 相等,且 equals() 方法返回 true,修改后的程序如下:
TreeSet 中的元素也是唯一的,定义如下:
因为 TreeSet 内部会自动按照升序对元素进行排列,所以添加到 TreeSet 集合中的元素必须具备排序的功能,现在我们创建一个类 A,同时将 A 的实例化对象保存到 TreeSet 中,代码如下:
报错原因是 A 不具备排序的功能,如何解决呢?让 A 实现 Comparable 接口即可,代码如下所示:
Set 接口的定义为:
public interface Set<E> extends Collection<E>实际开发中也不能直接实例化 Set,需要对其实现类进行实例化再完成业务操作。Set 的常用实现类主要有 HashSet、LinkedHashSet、TreeSet。
Set接口的实现类
HashSet 是开发中经常使用到的实现类,存储一组无序且唯一的对象,这里的无序是指元素的存储顺序和遍历顺序不一致。HashSet 的定义为:
public class HashSet<E> extends AbstreactSet<E>
implements Set<E>, Cloneable, java.io.Serializable
下列代码演示了 HashSet 的使用。
public class Test {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add("Hello");
hashSet.add("World");
hashSet.add("Java");
hashSet.add("Hello");
System.out.println("hashSet的长度:"+hashSet.size());
System.out.println("遍历hashSet");
Iterator iterator = hashSet.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+",");
}
hashSet.remove("World");
System.out.println("删除之后遍历hashSet");
iterator = hashSet.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+",");
}
}
}
运行结果为:
hashSet的长度:3
遍历hashSet
Java,Hello,World,
删除之后遍历hashSet
Java,Hello,
LinkedHashSet 是 Set 的另外一个子接口,可以存储一组有序且唯一的元素。这里的有序是指元素的存储顺序和遍历顺序是一致的。
LinkedHashSet 的定义为:
public class LinkedHashSet<E> extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable
下列代码演示了 LinkedHashSet 的使用:
public class Test {
public static void main(String[] args) {
LinkedHashSet linkedHashSet = new LinkedHashSet();
linkedHashSet.add("Hello");
linkedHashSet.add("World");
linkedHashSet.add("Java");
linkedHashSet.add("Hello");
System.out.println("linkedHashSet的长度:"+linkedHashSet.size());
System.out.println("遍历linkedHashSet");
Iterator iterator = linkedHashSet.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+",");
}
linkedHashSet.remove("World");
System.out.println("删除之后遍历linkedHashSet");
iterator = linkedHashSet.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+",");
}
}
}
运行结果为:
linkedHashSet的长度:3
遍历linkedHashSet
Hello,World,Java,
删除之后遍历linkedHashSet
Hello,Java,
我们对集合执行了添加两个“Hello”的操作,但是只保存了一个。这是因为 LinkedHashSet 集合的元素是唯一的,即不能出现两个相等的元素。字符串如此,其他对象也是一样的。我们定义一个 A 类,将类的实例化对象存入集合,代码为:
public class Test {
public static void main(String[] args) {
LinkedHashSet set = new LinkedHashSet();
set.add(new A(1));
set.add(new A(1));
Iterator iterator = set.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+",");
}
}
}
class A{
private int num;
public A(int num) {
this.num = num;
}
@Override
public String toString() {
return "A [num=" + num + "]";
}
}
运行结果为:
A [num=1],A [num=1],
你可以看到两个 A 对象都保存到了集合中,也就是说当前集合不认为这两个对象相等。那么程序是如何来鉴别两个对象是否相等的呢?通过继承自 Object 类的 equals() 方法来判断,Object 类的 equals() 方法定义如下:
public boolean equals(Object obj){
return (this == obj);
}
“==”表示比较两个对象的内存地址,所以虽然两个 A 对象的 num 值相等,也就是从内容的角度来看是相等的,但是内存地址不同,所以程序会认为是不相等的两个对象。现在有一个需求,只要两个对象的 num 值相等,就认为是同一个对象,那如何修改代码呢?说明一下用 LinkedHashSet 判断两个对象是否相等的原理,首先会判断两个对象的 hashCode 是否相等,什么是hashCode?简单来说就是将对象的内部信息(如内存地址、属性值等),通过某种特定规则转换成一个散列值,也就是该对象的 hashCode。两个不同对象的 hashCode 可能相等,但是 hashCode 不相等的两个对象一定不是同一个对象。
所以集合在判断两个对象是否相等时,会先比较它们的 hashCode,如果不相等,则认为不是同一个对象,可以添加。如果 hashCode 相等,还不能认为两个对象就是相等的,需要通过 equals() 方法进一步判断。如果 equals() 方法为 true,则不会重复添加;如果 equals() 方法为 false,则正常添加。先判断 hashCode 是否相等可以减少 equals() 方法的调用,提高效率。所以两个 A 相等的前提是 hashCode 相等,且 equals() 方法返回 true,修改后的程序如下:
class A{
……
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return true;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return 1;
}
}
再次运行程序,结果为:
A [num=1],
程序显示只存储了一个 A 对象,在 Set 的子接口中,除了 LinkedHashSet 可以存放有序元素之外,TreeSet 中保存的元素也是有序的,并且 TreeSet 的有序和 LinkedHashSet 的有序有所不同。LinkedHashSet 的有序是指元素的存储顺序和遍历顺序是一致的,即元素按什么顺序存进去,遍历时就按什么顺序输出。TreeSet 的有序是指集合内部会自动给所有的元素按照升序进行排列,即无论存入元素的顺序是什么,遍历时会按照升序进行输出。TreeSet 中的元素也是唯一的,定义如下:
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
下面的程序演示了 TreeSet 的使用:
public class Test {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
treeSet.add(1);
treeSet.add(3);
treeSet.add(6);
treeSet.add(2);
treeSet.add(5);
treeSet.add(4);
treeSet.add(1);
System.out.println("treeSet的长度:"+treeSet.size());
System.out.println("遍历treeSet");
Iterator iterator = treeSet.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+",");
}
treeSet.remove(5);
System.out.println("删除之后遍历treeSet");
iterator = treeSet.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+",");
}
}
}
运行结果为:
treeSet的长度:6
遍历treeSet
1,2,3,4,5,6,
删除之后遍历treeSet
1,2,3,4,6,
因为 TreeSet 内部会自动按照升序对元素进行排列,所以添加到 TreeSet 集合中的元素必须具备排序的功能,现在我们创建一个类 A,同时将 A 的实例化对象保存到 TreeSet 中,代码如下:
public class Test {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
treeSet.add(new A(1));
treeSet.add(new A(3));
treeSet.add(new A(6));
treeSet.add(new A(2));
treeSet.add(new A(5));
treeSet.add(new A(4));
treeSet.add(new A(1));
System.out.println("treeSet的长度:"+treeSet.size());
System.out.println("遍历treeSet");
Iterator iterator = treeSet.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+",");
}
treeSet.remove(new A(5));
System.out.println("删除之后遍历treeSet");
iterator = treeSet.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+",");
}
}
}
class A{
private int num;
public A(int num) {
this.num = num;
}
}
运行结果如下图所示:
报错原因是 A 不具备排序的功能,如何解决呢?让 A 实现 Comparable 接口即可,代码如下所示:
class A implements Comparable{
……
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
/**
* A.compareTo(B)
* 返回值:
* 1表示A大于B
* 0表示A等于B
* -1表示A小于B
*/
A a = (A) o;
if(this.num > a.num) {
return 1;
}else if(this.num == a.num) {
return 0;
}else {
return -1;
}
}
@Override
public String toString() {
return "A [num=" + num + "]";
}
}
再次运行,结果为:
treeSet的长度:6
遍历treeSet
A [num=1],A [num=2],A [num=3],A [num=4],A [num=5],A [num=6],
删除之后遍历treeSet
A [num=1],A [num=2],A [num=3],A [num=4],A [num=6],
ICP备案:
公安联网备案: