一 :Set和List的区别
注意:有序指元素存储顺序和取出顺序
List: 有序(存储顺序和取出顺序一致),可重复。
Set: 无序(存储顺序和取出顺序不一致),唯一。
问题:为什么存储字符串的时候,字符串相同的内容只能存储一个呢?代码如下:
package it.set; import java.util.HashSet; /** * HaehSet字符串存储并遍历 * 问题:为什么存储字符串的时候,字符串相同的内容只能存储一个呢? * 通过查看add的源码我们发现,这个方法的底层依赖HashCode()和equals()两个方法; * 步骤: * 首先比较哈希值 * 如果相同,继续走,比较地址值或者equals(); * 如果不同,就直接添加到集合中 * 按照方法的步骤来说: * 先看HashCode()值是否相同, * 相同 :继续走equals()方法, * 返回true,说明元素重复,就不添加。 * 返回false,说明元素不重复,就添加到集合中。 * 不同:就直接把元素添加到集合中。 * 如果类没有重写这两个方法,默认使用的是Obiect()的,一般来说不会相同。 * 而String就重写了HashCode()和equals()方法,它就可以把内容相同的字符串去掉,只留下一个。 */ public class HashSetTest { public static void main(String[] args) { //创建集合对象, HashSet<String> set = new HashSet<String>(); //添加元素 set.add("hello"); set.add("world"); set.add("java"); set.add("world"); //遍历元素 for(String s:set){ System.out.println(s); } } }
二 Set集合及他的子类HashSet和TreeSet
A:HashSet 特点:不保证Set的迭代顺序,特别是他不保证该顺序恒久不变。
问题:HashSet如何保证元素唯一性?
底层数据结构是哈希表(元素是链表的数组)
哈希表依赖于HashCode()和equals()两个方法;
练习:存储自定义对象,并保证唯一性/如果两个对象成员变量值都相同,则为同一元素。代码如下:
package Set.HashSet; import java.util.HashSet; /** * 需求:存储自定义对象,并保证唯一性/如果两个对象成员变量值都相同,则为同一元素。 * * 如果不重写学生类中的方法,则默认使用的是Object类,这个时候, * 的哈希值是不会一样的,根本就不会继续判断,执行了添加操作。 * * * 由于HashSet集合的底层是哈希表结构, * 而哈希表结构底层依赖HashCode()和equals()两个方法; * 如果你认为对象的成员变量值相同,即为同一个对象的话,你就应该重写这两个方法, * 如何重写呢?不用担心自动生成即可 * */ public class HashSetDome01 { public static void main(String[] args) { HashSet<StudentHashset> set = new HashSet<StudentHashset>(); //首先定义对象 StudentHashset s1 = new StudentHashset("唐僧",30); StudentHashset s2 = new StudentHashset("孙悟空",29); StudentHashset s3 = new StudentHashset("猪八戒",28); StudentHashset s4 = new StudentHashset("唐僧",30); StudentHashset s5 = new StudentHashset("沙和尚",27); StudentHashset s6 = new StudentHashset("唐僧",27); //添加元素 set.add(s1); set.add(s2); set.add(s3); set.add(s4); set.add(s5); set.add(s6); //遍历元素 for(StudentHashset s:set){ System.out.println(s.getName()+"======"+s.getAge()); } } }
对应的学生类,需要重写HashCode()和equals()方法
package Set.HashSet; public class StudentHashset { private String name; private int age; public StudentHashset(){ } public StudentHashset(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; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; StudentHashset other = (StudentHashset) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }B:TreeSet 特点:排序和唯一 ,能够让元素按照某种规则进行排序,1)自然排序 2)比较器排序(comparator接口)
第一 :自然排序(要想实现自然排序,必须实现Comparable<E>接口),代码如下:
package Set.TreeSet; import java.util.TreeSet; /**** * TreeSet 存储自定义对象并保证排序和唯一 * A: 你没有告诉我怎么排序? * 自然排序,安照年龄从小到大排序 * B: 元素什么情况下算唯一,你也没告诉我? * 成员变量值都相同即为同一个元素 */ public class TreeSetDome02 { public static void main(String[] args) { //创建集合对象 TreeSet<StudentTreeset> ts = new TreeSet<StudentTreeset>(); //创建元素 StudentTreeset s1 = new StudentTreeset("zhangxinxin",25); StudentTreeset s2 = new StudentTreeset("lilibo",30); StudentTreeset s3 = new StudentTreeset("zhangtao",19); StudentTreeset s4 = new StudentTreeset("zhangxinxin",20); StudentTreeset s5 = new StudentTreeset("lilibo",30); StudentTreeset s6 = new StudentTreeset("guojiali",26); StudentTreeset s7 = new StudentTreeset("aili",19); //添加元素 ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); ts.add(s5); ts.add(s6); ts.add(s7); for(StudentTreeset s:ts){ System.out.println(s.getName()+"---"+s.getAge()); } } }
如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口
package Set.TreeSet; /** * 如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口。 * */ public class StudentTreeset implements Comparable<StudentTreeset>{ private String name; private int age; public StudentTreeset(){ } public StudentTreeset(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; } @Override public int compareTo(StudentTreeset s) { //这里返回什么,其实应该根据我的排序规则来做 //按照年龄排序,主要条件(添加那个对象,那个对象叫this,它要和其他元素进行比较) int num = this.age - s.age; // return num; //次要条件 年龄相同的时候还要去看姓名是否相同 //如果年龄和姓名都相同,才是同一个元素 int num2 = num == 0 ? this.name.compareTo(s.name) : num; //如果年龄相同,继续比姓名,否则返回num年龄; return num2; } }
第二:比较器排序(comparator接口)
package Set.TreeSet; import java.util.Comparator; import java.util.TreeSet; /**** * TreeSet 存储自定义对象并保证排序和唯一 * 比较器排序,如果一个方法的参数是接口,那么真正要的接口的实现类对象 */ public class TreeSetDome03 { public static void main(String[] args) { //创建集合对象 //TreeSet<StudentTreeset> ts = new TreeSet<StudentTreeset>(); //自然排序 //public TreeSet(Comparator comparator); //比较器排序 //TreeSet<StudentTreeset> ts = new TreeSet<StudentTreeset>(new MyComparator()); TreeSet<StudentTreeset> ts = new TreeSet<StudentTreeset>(new Comparator<StudentTreeset>(){ @Override public int compare(StudentTreeset s1, StudentTreeset s2) { //姓名长度 int num = s1.getName().length()-s2.getName().length(); //姓名 int num1 = num==0?s1.getName().compareTo(s2.getName()):num; //年龄 int num2 = num1==0?s1.getAge()-s2.getAge():num1; return num2; } }); //创建元素 StudentTreeset s1 = new StudentTreeset("zhangxinxin",25); StudentTreeset s2 = new StudentTreeset("lilibo",30); StudentTreeset s3 = new StudentTreeset("zhangtao",19); StudentTreeset s4 = new StudentTreeset("zhangxinxin",20); StudentTreeset s5 = new StudentTreeset("lilibo",30); StudentTreeset s6 = new StudentTreeset("guojiali",26); StudentTreeset s7 = new StudentTreeset("aili",19); //添加元素 ts.add(s1); ts.add(s2); ts.add(s3); ts.add(s4); ts.add(s5); ts.add(s6); ts.add(s7); for(StudentTreeset s:ts){ System.out.println(s.getName()+"---"+s.getAge()); } } }
最后总结:TreeSet集合保证元素排序和唯一性原理
唯一性:是根据比较的返回值是不是0来决定
排序:
A:自然排序(元素具备比较性)
让元素所属的类实现自然排序接口Comparable
B:比较器排序(集合具备比较性)
让集合的构造方法接收一个比较器接口的子类对象Comparator