Collection子接口:其二 Set 组接口
- Set接口是Collection的子接口,Set没有提供额外的方法
- Set集合中不允许包含重复的元素,如果重复添加,只保留最新添加的那一个
- Set判断两个元素是否相同不是 == 判断,而是取equals()方法
Set主要实现类:
HashSet、LinkedHashSet、TreeSet
- HashSet Set主要接口实现类、线程不安全的、可存储Null值
- LinkedHashSet 继承了HashSet、遍历内部数据,可按照添加时的顺序遍历
- TreeSet 底层存储结构是红黑树【二叉树】,按照指定属性进行排序
Set底层的说明,红康老师讲的还是不解,决定留在数据结构篇再来细谈
https://www.bilibili.com/video/BV1Kb411W75N?p=537
底层算法的哈希码
https://www.cnblogs.com/mindzone/p/12736516.html
1 public class CollectionTest { 2 public static void main(String[] args) { 3 Set set = new HashSet(); 4 5 set.add("阿伟"); 6 set.add("杰哥"); 7 set.add("1376"); 8 set.add("先辈"); 9 set.add("Van"); 10 set.add("佟大为"); 11 set.add("1376"); 12 set.add(new Student("小明",12)); 13 set.add(new Student("小明",12)); 14 15 Iterator iterator = set.iterator(); 16 17 while(iterator.hasNext()){ 18 Object next = iterator.next(); 19 System.out.println(next); 20 } 21 // 阿伟 Van 1376 佟大为 杰哥 先辈 22 // 无序性不是遍历的结果没有顺序(不是随机性) 存储的数据不是按照索引顺序排列的,根据数据的Hash值添加 23 // 不可重复性 这里添加了同一字符串1376,从哈希码可以判断是一样,set集合只会保留其中一个 24 25 // 如果是对象就不一样了,这里依然会保留两个同属性对象,底层哈希码是不一样的 26 // 再重写equals & hashCode 之后只保留一个对象了, 调用add方法时,set会调用对象的equals方法判断,为true将不会添加 27 } 28 } 29 30 31 class Student{ 32 private String name; 33 private Integer age; 34 35 public String getName() { 36 return name; 37 } 38 39 public void setName(String name) { 40 this.name = name; 41 } 42 43 public Student(String name, Integer age) { 44 this.name = name; 45 this.age = age; 46 } 47 48 public Integer getAge() { 49 return age; 50 } 51 52 public void setAge(Integer age) { 53 this.age = age; 54 } 55 56 @Override 57 public String toString() { 58 return "Student{" + 59 "name='" + name + '\'' + 60 ", age=" + age + 61 '}'; 62 } 63 64 @Override 65 public boolean equals(Object o) { 66 if (this == o) return true; 67 if (!(o instanceof Student)) return false; 68 Student student = (Student) o; 69 return Objects.equals(getName(), student.getName()) && 70 Objects.equals(getAge(), student.getAge()); 71 } 72 73 @Override 74 public int hashCode() { 75 return Objects.hash(getName(), getAge()); 76 } 77 }
LinkedHashSet,是HashSet的子类
添加数据的同时每个数据维护了两个引用,记录前一个数据和后一个数据
对于频繁的遍历操作,LinkedHashSet效率高于HashSet
public class CollectionTest { public static void main(String[] args) { Set set = new LinkedHashSet(); set.add("阿伟"); set.add("杰哥"); set.add("1376"); set.add("先辈"); set.add("Van"); set.add("佟大为"); set.add("1376"); Iterator iterator = set.iterator(); while(iterator.hasNext()){ Object next = iterator.next(); System.out.println(next); } } }
TreeSet存储的数据只能是同一个类型的数据
自定义数据类型或者其他的基本类型封装类
TreeSet方式的判断是以Comparable接口实现的compareTo方法操作的【自然排序方式】
0 表示相同 TreeSet将会认为是重复,不再添加重复的元素
1 public class CollectionTest { 2 public static void main(String[] args) { 3 Set set = new TreeSet(); 4 5 set.add(new Person("Jimmy",17)); 6 set.add(new Person("Mike",13)); 7 set.add(new Person("Jim",15)); 8 set.add(new Person("Jack",12)); 9 set.add(new Person("Jerry",18)); 10 set.add(new Person("Jack",56)); 11 12 Iterator iterator = set.iterator(); 13 14 while(iterator.hasNext()){ 15 Object next = iterator.next(); 16 System.out.println(next); 17 } 18 } 19 } 20 21 22 // 需要实现Comparable接口 23 class Person implements Comparable{ 24 private String name; 25 private Integer age; 26 27 public Person(String name, Integer age) { 28 this.name = name; 29 this.age = age; 30 } 31 32 public String getName() { 33 return name; 34 } 35 36 public void setName(String name) { 37 this.name = name; 38 } 39 40 public Integer getAge() { 41 return age; 42 } 43 44 public void setAge(Integer age) { 45 this.age = age; 46 } 47 48 @Override 49 public String toString() { 50 return "Person{" + 51 "name='" + name + '\'' + 52 ", age=" + age + 53 '}'; 54 } 55 56 // 重写此方法 57 @Override 58 public int compareTo(Object o) { 59 if (o instanceof Person){ 60 Person p = (Person)o; 61 int compare = this.name.compareTo(p.name); // 掉用String的compareTo方法,不是本地的 62 if (compare != 0) return compare; 63 else return Integer.compare(this.age,p.age); // 如果相同,再compare第二属性 64 }else throw new RuntimeException("装入的类型不匹配!"); 65 } 66 }
详细红黑树可以参考自视频里面的这篇链接,作者博客已经搬迁了啊
https://www.yycoding.xyz/post/2014/3/27/introduce-red-black-tree
除了上面的自然排序,也支持定制排序
1 public class CollectionTest { 2 public static void main(String[] args) { 3 Comparator comparator = new Comparator() { 4 // 按照年龄从小到大排列,一样剔除 5 @Override 6 public int compare(Object o1, Object o2) { 7 if (o1 instanceof Person && o2 instanceof Person){ 8 Person p1 = (Person)o1; 9 Person p2 = (Person)o2; 10 return Integer.compare(p1.getAge(),p2.getAge()); 11 }else throw new RuntimeException("类型不匹配!"); 12 } 13 }; 14 15 // 如果定义了定制排序,创建Set集合初始化时,注入定制比较器,TreeSet就会默认使用此比较 16 Set set = new TreeSet(comparator); 17 18 set.add(new Person("Jimmy",17)); 19 set.add(new Person("Mike",13)); 20 set.add(new Person("Jim",15)); 21 set.add(new Person("Jack",12)); 22 set.add(new Person("Jerry",18)); 23 set.add(new Person("Jack",56)); 24 25 Iterator iterator = set.iterator(); 26 27 while(iterator.hasNext()){ 28 Object next = iterator.next(); 29 System.out.println(next); 30 } 31 } 32 } 33 34 35 // 需要实现Comparable接口 36 class Person implements Comparable{ 37 private String name; 38 private Integer age; 39 40 public Person(String name, Integer age) { 41 this.name = name; 42 this.age = age; 43 } 44 45 public String getName() { 46 return name; 47 } 48 49 public void setName(String name) { 50 this.name = name; 51 } 52 53 public Integer getAge() { 54 return age; 55 } 56 57 public void setAge(Integer age) { 58 this.age = age; 59 } 60 61 @Override 62 public String toString() { 63 return "Person{" + 64 "name='" + name + '\'' + 65 ", age=" + age + 66 '}'; 67 } 68 69 // 重写此方法 70 @Override 71 public int compareTo(Object o) { 72 if (o instanceof Person){ 73 Person p = (Person)o; 74 int compare = this.name.compareTo(p.name); // 掉用String的compareTo方法,不是本地的 75 if (compare != 0) return compare; 76 else return Integer.compare(this.age,p.age); // 如果相同,再compare第二属性 77 }else throw new RuntimeException("装入的类型不匹配!"); 78 } 79 }
没有写定制排序才会按照equals方法排序