5 Set
5.1 Set 特点和基本操作
Set 接口的特点是元素不可以重复,无顺序
Set 接口中所有的操作都继承自 Collection 接口,也就是说,Set 接口没有自己特有的操作,其所有操作都来源于父接口 Collection。因此,它具有Collection 接口中定义的那些诸如 add、remove 等方法。
特别要注意的是,由于 Set 集合中的元素没有顺序,因此 Set 集合中的元素没有下标的概念。因此,和 List 接口不同,Set 接口中没有定义与下标相关的操作。
5.2 遍历
先介绍一个 Set 接口的实现类,HashSet。我们利用这个类来测试 Set 接口的遍历。
Set 接口中没有跟下标相关的方法,也就是说,Set 接口中没有类似 List 接口中的 get方法,因此,无法使用跟下标紧密联系的 for 循环遍历。但是,Set 接口可以使用迭代遍历。Collection 接口中定义了 iterator 方法,因此 Set 接口中也包含了这个方法。对于 Set 集合来说(尤其是 JDK1.5 以前的版本),只能采用迭代器的方式来遍历。
public class TestSet { public static void main(String[] args) { Set set = new HashSet(); set.add("hello"); set.add("world"); set.add("java"); //加入重复元素是,add 方法会返回 false set.add("hello"); //迭代遍历 Iterator iter = set.iterator(); while(iter.hasNext()){ Object value = iter.next(); System.out.println(value); } } }
要注意的是,迭代遍历输出的结果为:
hello
java
world
注意到对 set 调用了两次 add(“hello”)方法,但是输出结构只有一个 hello 字符串。同时,注意到输出结果的排列顺序与加入 set 的顺序完全无关。这就是 Set 集合的特点:元素无顺序,不可以重复。
5.3 实现类
对于 Set 集合的基本操作,相对而言比较容易掌握。对于 Set 接口而言,比较难掌握的地方在于 Set 接口的实现类相关内容。下面这部分内容是学习 Set 接口的重点。
HashSet 实现了 Set 接口,因此要求元素不可以重复。那么,HashSet 是怎么来判断元素是否可以重复的呢?
总结一下,如果要正常使用 HashSet 存放对象,为了保证对象的内容不重复,则要求这个对象满足:
1. 覆盖 equals 方法。要求相同的对象,调用 equals 方法返回 true。
2. 覆盖 hashCode 方法。要求,相同对象的 hashCode 相同,不同对象的 hashCode 尽量不同。
import java.util.*; class Student{ private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int hashCode() { return age + name.hashCode(); } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student stu = (Student) obj; if ( (this.age == stu.age)&& (this.name.equals(stu.name)) ){ return true; }else{ return false; } } public String toString(){ return name + " " + age; } } public class TestHashSet { public static void main(String[] args) { Set set = new HashSet(); set.add(new Student("Tom", 18)); set.add(new Student("Jim", 20)); set.add(new Student("Fred", 22)); set.add(new Student("Tom", 18)); Iterator iter = set.iterator(); while(iter.hasNext()){ System.out.println(iter.next()); } } }
输出结果如下:
Fred 22
Jim 20
Tom 18
注意,add()方法被调用了四次,但是遍历 set 集合时,只读到了三个元素。
5.3.2 LinkedHashSet
HashSet 的特点是元素不可重复且元素无顺序。某些情况下,我们依然需要元素不可以重复,但是希望按照我们加入 Set 的先后顺序来加入这些元素。这个时候,我们就可以使用LinkedHashSet。
import java.util.*; public class TestLinkedHashSet { public static void main(String args[]){ Set set = new LinkedHashSet(); set.add("hello"); set.add("world"); set.add("java"); set.add("hello"); Iterator iter = set.iterator(); while(iter.hasNext()){ System.out.println(iter.next()); } } }
输出结果如下:
hello
world
java
我们可以看到,字符串的打印顺序和它们添加到 LinkedHashSet 中的顺序是一致的。同时,hello 字符串被添加了两次,但只打印了一次。要注意的是,如果要使用 LinkedHashSet 的话,也必须正确的覆盖对象的 hashCode 和equals 方法。