(资料图片仅供参考)
Set 接口基本介绍
Set
接口是Collection
接口的一个子接口,其主要特点如下:
不允许重复元素:
Set
接口的实现类不会包含重复的元素。更正式地说,不包含任何一对使得e1.equals(e2)
成立的元素e1
和e2
,并且最多只能有一个null
元素。当尝试添加重复元素时,添加操作将被忽略。无序性:
Set
接口取出元素的顺序和添加元素的顺序不一致(但是每次取出的顺序是固定的),即无法通过索引访问Set
中的元素。
Set
接口的常用实现类有HashSet
、TreeSet
和LinkedHashSet
。可以使用迭代器和增强 for 循环遍历元素,但是不能使用普通 for 循环(不能使用索引)。下面的代码以其实现类HashSet
演示Set
接口的特点。
public class TestSet() { public static void main(String[] args) { Set set = new HashSet(); set.add("Phoebe"); set.add("Monica"); set.add("Phoebe"); // 重复 set.add("Ross"); set.add("Chandler"); set.add("Rachel"); set.add(null); set.add(null); // 再次添加 null // 10 次输出结果均为:set=[Phoebe, null, Rachel, Ross, Chandler, Monica] // 重复的 Phoebe 和 null 都只输出了一个, // 取出顺序和添加顺序不一致但每次取出顺序都是相同的。 for (int i = 0; i < 10; i++) { System.out.println("set=" + set); } }}
注意:如果Set
集合的元素是可变对象时,必须要小心。如果以影响equals()
方法比较结果的方式修改Set
中的元素的值,集合的行为是未指定的。这种情况的特例是Set
不能将自身作为元素。以下以一个简单的 Person 类来测试修改HashSet
中元素的值使得两个元素equals()
方法比较结果相同时的情况。
public class Person { private String name; public Person() { } public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return name.equals(person.name); } @Override public int hashCode() { return Objects.hash(name); } @Override public String toString() { return "Person{" + "name="" + name + "\"" + "}"; }}
public class TestModifyValue() { public static void main(String[] args) { Set set = new HashSet(); // 创建 3 个 Person 实例,其中 rose1.equals(rose2) 成立 Person jack = new Person("Jack"); Person rose1 = new Person("Rose"); Person rose2 = new Person("Rose"); // 将其添加到 set 中 set.add(jack); set.add(rose1); set.add(rose2); // 可以看到只添加成功一个姓名为 Rose 的 Person 实例,其应为 rose1。 System.out.println("set = " + set); // set = [Person{name="Rose"}, Person{name="Jack"}] // 将 set 添加到自身中,未添加成功 set.addAll(set); System.out.println("set = " + set); // set = [Person{name="Rose"}, Person{name="Jack"}] // 将 rose1 的 name 属性修改为 Jack rose1.setName("Jack"); System.out.println(rose1.equals(jack)); // true // 可以看到此时 set 集合中仍有两个对象,其 equals() 方法比较结果相同。 System.out.println("set = " + set); // set = [Person{name="Jack"}, Person{name="Jack"}] }}
Set 接口常用方法
Set
接口和List
接口一样,也是Collection
接口的子接口,因此常用方法和Collection
接口一样。但需要注意由于Set
接口不包含重复元素,所以addAll()
方法的参数也是Set
时,addAll
操作会有效地修改该集合,使其值为两个集合的并集。
public class SetAddAll() { public static void main(String[] args) { Set set = new HashSet(); set.add("Phoebe"); set.add("Monica"); set.add("Ross"); System.out.println("set = " + set); // set = [Phoebe, Ross, Monica] Set set1 = new HashSet(); set1.add("Phoebe"); set.add("Chandler"); set.addAll(set1); System.out.println("set = " + set); // set = [Phoebe, Ross, Chandler, Monica] }}
Set 接口遍历元素方式
Set
接口遍历元素的方式和Collection
接口的一样,可以使用迭代器和增强 for 循环来遍历元素,但不能通过索引的方式来遍历元素。
public class SetThroughElements() { public static void main(String[] args) { Set set = new HashSet(); set.add("Phoebe"); set.add("Monica"); set.add("John"); System.out.println("---使用迭代器---"); Iterator iterator = set.iterator(); while (iterator.hasNext()) { Object obj = iterator.next(); System.out.println("obj = " + obj); } System.out.println("---使用增强 for---"); for (Object o :set) { System.out.println("o = " + o); } // 不能使用普通 for 循环,因为 Set 不支持索引 }}