概述
Set接口作为Collection的子接口,按理来说应该在原来接口的基础增加更多的方法,但是Set这个子接口呢,并没有提供额外的方法,只不过是在原来的基础上,对数据的要求更加严格了。
Set接口的特点:
既然Set没有在Collection的基础上添加新的方法,那么其常用方法也是可以参考Collection的常用方法的
Collection的常用方法可参考本文
与Collection集合的遍历方式一样,Set集合也是支持foreach和Iterator两种方式遍历元素的。
代码实例package com.atguigu.demo.Set; import java.util.Iterator; import java.util.linkedHashSet; public class linkedSet { public static void main(String[] args) { //创建linkedHashSet集合 linkedHashSet<String> set = new linkedHashSet<>(); //添加元素 set.add("张三"); set.add("李四"); set.add("王五"); set.add("张三"); //元素个数 System.out.println("元素个数:" + set.size()); //增强for遍历元素 for (String name : set) { System.out.println(name); } //使用iterator Iterator<String> iterator = set.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } }Set的实现类
Set接口有很多的实现类,下面我们介绍下其常用的实现类: HashSet、TreeSet、linkedHashSet
HashSet 基本介绍HashSet是Set接口最常使用的实现类,其底层是通过HashMap实现的。(HashMap可以看这个文章)
源码如下:
public HashSet() { map = new HashMap<>(); }
HashMap的底层物理实现是一个Hash表,那这样的话,存入元素的时候,就会根据Hash算法来存储了。即当向HashSet集合中存入一个元素时,HashSet会先判断该对象的hashCode值,然后根据hashCode值决定该对象在HashSet中的存储位置:
所以存储到HashSet的元素要重写hashCode和equals方法。
代码示例package com.atguigu.demo.Set; public class Employee { //自定义属性 private String name; private MyDate birthday; //构造器 public Employee(String name, MyDate birthday) { super(); this.name = name; this.birthday = birthday; } public Employee() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public MyDate getBirthday() { return birthday; } public void setBirthday(MyDate birthday) { this.birthday = birthday; } //重写hashCode @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((birthday == null) ? 0 : birthday.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } //重写equals @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Employee other = (Employee) obj; if (birthday == null) { if (other.birthday != null) return false; } else if (!birthday.equals(other.birthday)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } @Override public String toString() { return "姓名:" + name + ", 生日:" + birthday; } } //hashSet测试 import java.util.HashSet; public class TestHashSet { @SuppressWarnings("all") public static void main(String[] args) { HashSet<Employee> set = new HashSet<>(); //创建HashSet集合 set.add(new Employee("张三", new MyDate(1990,1,1))); //重复元素无法添加,因为MyDate和Employee重写了hashCode和equals方法 set.add(new Employee("张三", new MyDate(1990,1,1))); set.add(new Employee("李四", new MyDate(1992,2,2))); for (Employee object : set) { System.out.println(object); } } }linkedHashSet 基本介绍
linkedHashSet是HashSet的子类,所以它的很多东西都是继承自HashSet的,这里不再说明,但是相对于HashSet,它在结点中增加两个属性before和after维护了结点的前后添加顺序,这是因为其底层采用了链表+哈希表的结构。
源码如下:
HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new linkedHashMap<>(initialCapacity, loadFactor); }
所以linkedHashSet可以保证元素的插入顺序。
代码示例因为维护了一个链表,存储、查找的性能略低于HashSet,但遍历时性能高于HashSet(根据链表进行遍历)。
import java.util.Iterator; import java.util.linkedHashSet; public class linkedHashSet { public static void main(String[] args) { //创建linkedHashSet集合 linkedHashSet<String> set = new linkedHashSet<>(); //添加元素 set.add("张三"); set.add("李四"); set.add("王五"); set.add("张三"); //元素个数 System.out.println("元素个数:" + set.size()); //增强for遍历元素 for (String name : set) { System.out.println(name); } } }TreeSet 基本介绍
与前面两种实现类不同,TreeSet底层是基于红黑树结构实现的,另外TreeSet里的元素是有序的,而实现这个有序的特征,是需要TreeSet里的元素实现排序,其实现排序的形式有两种:自然排序和定制排序。
因为放入其中的元素的类必须实现Comparable接口,所以在元素不能为null。
自然排序
这个默认的排序,让待添加的元素类型实现Comparable接口,并重写compareTo方法(数值型按数值大小排列,字符按码值排列,Date、Time按时间戳的大小排列…默认升序)
对于 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过 compareTo(Object obj) 方法比较返回值为0。
定制排序
如果自然排序(Comparable)规则不符合我们的排序需求,或者元素的类型没有实现Comparable接口。那么在创建TreeSet时,可以单独指定一个Comparator的对象,根据我们自己的需求进行自定义排序。
使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0。
public class Student { //自定义属性 private int id; private String name; //构造器 public Student(int id, String name) { super(); this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } //省略了name属性的get/set @Override public String toString() { return "Student [id=" + id + ", name=" + name + "]"; } } import org.junit.Test; import java.util.Comparator; import java.util.TreeSet; public class TestTreeSet { @Test public void test1(){ //MyDate myDate = new MyDate(1990,1,1); //创建TreeSet集合 TreeSet<String> set = new TreeSet<>(); //TreeSet set2 = new TreeSet<>(); //ClassCastException:com.atguigu.demo.Set.MyDate cannot be cast to java.lang.Comparable //添加元素 set.add("zhangsan"); //String它实现了java.lang.Comparable接口 set.add("zisi"); set.add("wangwu"); set.add("hangsan"); //set2.add(myDate); //System.out.println(set2); System.out.println("元素个数:" + set.size()); for (String str : set) { System.out.println(str); } } @Test public void test2(){ //创建集合,并指定Comparator的对象 TreeSet<Student> set = new TreeSet(new Comparator<Student>(){ //那么在创建TreeSet时,可以单独指定一个Comparator的对象 @Override public int compare(Student o1, Student o2) { return o1.getId() - o2.getId(); } }); //添加学生 set.add(new Student(3,"张三")); set.add(new Student(1,"李四")); set.add(new Student(2,"王五")); set.add(new Student(3,"张三风")); //元素个数 System.out.println("元素个数:" + set.size()); //遍历 for (Student stu : set) { System.out.println(stu); } } }文章转自:吃透Java集合中的Set集合必备文章,快快收藏_Java-考高分网 (kaotop.com)