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],