1、Set集合(理解)
(1)Set集合的特点
无序,唯一。
(2)HashSet集合(掌握)
A: 底层数据结构是哈希表(是一个元素为链表的数组)
B: 哈希表底层依赖两个方法: hashCode() 和 equals()
执行顺序:
首先比较哈希值是否相同
相同:继续执行equals()方法
返回true:元素重复了,不添加
返回false:直接把元素添加到集合
不同:就直接把元素添加到集合
1、人的类
1 package com.wyh.hashSet; 2 3 /** 4 * @author WYH 5 * @version 2019年11月17日 上午9:21:02 6 */ 7 public class Person { 8 private String name; 9 private int age; 10 11 public Person(String name, int age) { 12 super(); 13 this.name = name; 14 this.age = age; 15 } 16 public Person() { 17 super(); 18 // TODO Auto-generated constructor stub 19 } 20 21 public String getName() { 22 return name; 23 } 24 public void setName(String name) { 25 this.name = name; 26 } 27 public int getAge() { 28 return age; 29 } 30 public void setAge(int age) { 31 this.age = age; 32 } 33 34 //重写hashCode方法和equals方法 35 @Override 36 public int hashCode() { 37 final int prime = 31; 38 int result = 1; 39 result = prime * result + age; 40 result = prime * result + ((name == null) ? 0 : name.hashCode()); 41 return result; 42 } 43 @Override 44 public boolean equals(Object obj) { 45 if (this == obj) 46 return true; 47 if (obj == null) 48 return false; 49 if (getClass() != obj.getClass()) 50 return false; 51 Person other = (Person) obj; 52 if (age != other.age) 53 return false; 54 if (name == null) { 55 if (other.name != null) 56 return false; 57 } else if (!name.equals(other.name)) 58 return false; 59 return true; 60 } 61 62 63 }
2、测试类:
1 package com.wyh.hashSet; 2 3 import java.util.HashSet; 4 5 /** 6 * @author WYH 7 * @version 2019年11月17日 上午9:21:16 8 */ 9 public class HashSetDemo01 { 10 public static void main(String[] args) { 11 //创建HashSet集合 12 HashSet<Person> hs = new HashSet<Person>(); 13 14 //创建Person类 15 Person p1 = new Person("小虎",22); 16 Person p2 = new Person("小强",23); 17 Person p3 = new Person("小虎",22); 18 Person p4 = new Person("小美",21); 19 Person p5 = new Person("小雪",21); 20 21 //将对象添加到HashSet中去 22 hs.add(p1); 23 hs.add(p2); 24 hs.add(p3); 25 hs.add(p4); 26 hs.add(p5); 27 28 29 //遍历 30 for(Person p : hs) { 31 System.out.println(p.getName()+"---"+p.getAge()); 32 33 34 } 35 } 36 37 }
3、如下图,如果不重写hashCode()方法和equals()方法,运行结果,可以运行不报错,但是重复的元素也加入了。
4、重写后:
C:如何保证元素的唯一性呢?
由hashCode()和equals()保证的(如下图,流程)
D:开发的时候,代码非常的简单,自动生成即可。
E:HashSet存储字符串并遍历
1 package com.wyh.set; 2 3 import java.util.HashSet; 4 5 /** 6 * @author WYH 7 * @version 2019年11月16日 下午7:42:24 8 * 9 * HashSet在存储字符串的时候,为什么只存储相同的字符串只存储一个呢? 10 * 通过查看add方法的源码,我们发现这个方法的底层方法是依赖两个方法,hashCode()和equals() 11 * 12 * 13 */ 14 public class HashSetDemo01 { 15 public static void main(String[] args) { 16 HashSet<String> hs = new HashSet<String>(); 17 18 hs.add("Hello"); 19 hs.add("World"); 20 hs.add("Java"); 21 hs.add("World"); 22 23 for(String s : hs) { 24 System.out.println(s); 25 } 26 27 } 28 29 }
F:HashSet存储自定义对象并遍历(对象的成员变量值相同即为同一个元素)
(3)TreeSet集合
A:底层数据结构是红黑树(是一个自平衡的二叉树)
B: 保证元素的排序方式
a: 自然排序(元素具备比较性)
让元素所属的类实现Comparable接口(难点,需求会给出主要条件,但是需要我们分析次要条件,例如 需求是根据年龄的长度进行排序,但是我们还要考虑姓名的长度和内容)
学生类:
1 package com.wyh.treeSet; 2 3 /** 4 * @author WYH 5 * @version 2019年11月17日 上午11:13:45 6 */ 7 public class Student2 implements Comparable<Student2>{ 8 private String name; 9 private int age; 10 public Student2(String name, int age) { 11 super(); 12 this.name = name; 13 this.age = age; 14 } 15 public Student2() { 16 super(); 17 // TODO Auto-generated constructor stub 18 } 19 public String getName() { 20 return name; 21 } 22 public void setName(String name) { 23 this.name = name; 24 } 25 public int getAge() { 26 return age; 27 } 28 public void setAge(int age) { 29 this.age = age; 30 } 31 @Override 32 public int compareTo(Student2 s) { 33 int num = this.name.length() - s.name.length(); 34 //次要条件1 姓名长度一样,内容不一定一样 35 int num2 = num == 0 ? (this.name.compareTo(s.name)) : num; 36 //次要条件2 姓名长度和内容一样,年龄不一定一样 37 int num3 = num2 == 0 ? this.age - s.age : num2; 38 return num3; 39 40 } 41 42 43 44 45 }
测试类:
如果不重写自然排序方法,就会报错,因为没有给定如何排序,也没有保证唯一性
// java.lang.ClassCastException: com.wyh.treeSet.Student cannot be cast to
// java.lang.Comparable
1 package com.wyh.treeSet; 2 3 import java.util.TreeSet; 4 5 /** 6 * @author WYH 7 * @version 2019年11月17日 下午2:27:13 8 * 9 * 根据年龄的长度进行排序 10 */ 11 public class TreeSetDemo03 { 12 public static void main(String[] args) { 13 TreeSet<Student2> ts = new TreeSet<Student2>(); 14 15 // 创建学生对象 16 Student2 s1 = new Student2("王友虎", 22); 17 Student2 s2 = new Student2("赵以浩", 24); 18 Student2 s3 = new Student2("齐博源", 21); 19 Student2 s4 = new Student2("李先锋", 23); 20 Student2 s5 = new Student2("李宏灿", 22); 21 Student2 s6 = new Student2("薛长城", 23); 22 Student2 s7 = new Student2("黄天祥", 24); 23 Student2 s8 = new Student2("王友虎", 23); 24 25 // 添加到TreeSet中 26 ts.add(s1); 27 ts.add(s2); 28 ts.add(s3); 29 ts.add(s4); 30 ts.add(s5); 31 ts.add(s6); 32 ts.add(s7); 33 ts.add(s8); 34 35 // 遍历 36 // 如果不重写自然排序方法,就会报错,因为没有给定如何排序,也没有保证唯一性 37 // java.lang.ClassCastException: com.wyh.treeSet.Student cannot be cast to 38 // java.lang.Comparable 39 for (Student2 s : ts) { 40 System.out.println(s.getName() + "---" + s.getAge()); 41 } 42 43 } 44 45
b: 比较器排序(集合具备比较性)
让集合构造方法接收Comparator的实现类对象(但是我们一般用匿名内部类的方式实现)
方式1、我们不用内部类的方式实现:
定义一个类实现Comparator<T> 接口,重写compare方法:
1 package com.wyh.treeSet2; 2 3 import java.util.Comparator; 4 5 /** 6 * @author WYH 7 * @version 2019年11月17日 下午2:48:22 8 */ 9 public class MyComparator implements Comparator<Student2> { 10 11 @Override 12 public int compare(Student2 s1, Student2 s2) { 13 int num = s1.getName().length() - s2.getName().length(); 14 int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num; 15 int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2; 16 return num3; 17 } 18 19 20 21 }
测试类:
1 package com.wyh.treeSet2; 2 3 import java.util.Comparator; 4 import java.util.TreeSet; 5 6 /** 7 * @author WYH 8 * @version 2019年11月17日 下午2:27:13 9 * 10 * 根据年龄的长度进行排序 11 */ 12 public class TreeSetDemo03 { 13 public static void main(String[] args) { 14 // TreeSet<Student2> ts = new TreeSet<Student2>(); 15 TreeSet<Student2> ts = new TreeSet<Student2>(new MyComparator()); 16 17 //如果一个方法的参数是接口,那么真正要的是实现这个接口的实现类 18 //匿名内部类可以是实现 19 /* TreeSet<Student2> ts = new TreeSet<Student2>(new Comparator<Student2>(){ 20 @Override 21 public int compare(Student2 s1, Student2 s2) { 22 int num = s1.getName().length() - s2.getName().length(); 23 int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num; 24 int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2; 25 return num3; 26 } 27 });*/ 28 29 // 创建学生对象 30 Student2 s1 = new Student2("王友虎", 22); 31 Student2 s2 = new Student2("赵以浩", 24); 32 Student2 s3 = new Student2("齐博源", 21); 33 Student2 s4 = new Student2("李先锋", 23); 34 Student2 s5 = new Student2("李宏灿", 22); 35 Student2 s6 = new Student2("薛长城", 23); 36 Student2 s7 = new Student2("黄天祥", 24); 37 Student2 s8 = new Student2("王友虎", 23); 38 39 // 添加到TreeSet中 40 ts.add(s1); 41 ts.add(s2); 42 ts.add(s3); 43 ts.add(s4); 44 ts.add(s5); 45 ts.add(s6); 46 ts.add(s7); 47 ts.add(s8); 48 49 // 遍历 50 // 如果不重写自然排序方法,就会报错,因为没有给定如何排序,也没有保证唯一性 51 // java.lang.ClassCastException: com.wyh.treeSet.Student cannot be cast to 52 // java.lang.Comparable 53 for (Student2 s : ts) { 54 System.out.println(s.getName() + "---" + s.getAge()); 55 } 56 57 } 58 59 }
方式2、用匿名内部类的方式实现:
1 package com.wyh.treeSet2; 2 3 import java.util.Comparator; 4 import java.util.TreeSet; 5 6 /** 7 * @author WYH 8 * @version 2019年11月17日 下午2:27:13 9 * 10 * 根据年龄的长度进行排序 11 */ 12 public class TreeSetDemo03 { 13 public static void main(String[] args) { 14 // TreeSet<Student2> ts = new TreeSet<Student2>(); 15 // TreeSet<Student2> ts = new TreeSet<Student2>(new MyComparator()); 16 17 //如果一个方法的参数是接口,那么真正要的是实现这个接口的实现类 18 //匿名内部类可以是实现 19 TreeSet<Student2> ts = new TreeSet<Student2>(new Comparator<Student2>(){ 20 @Override 21 public int compare(Student2 s1, Student2 s2) { 22 int num = s1.getName().length() - s2.getName().length(); 23 int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num; 24 int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2; 25 return num3; 26 } 27 }); 28 29 // 创建学生对象 30 Student2 s1 = new Student2("王友虎", 22); 31 Student2 s2 = new Student2("赵以浩", 24); 32 Student2 s3 = new Student2("齐博源", 21); 33 Student2 s4 = new Student2("李先锋", 23); 34 Student2 s5 = new Student2("李宏灿", 22); 35 Student2 s6 = new Student2("薛长城", 23); 36 Student2 s7 = new Student2("黄天祥", 24); 37 Student2 s8 = new Student2("王友虎", 23); 38 39 // 添加到TreeSet中 40 ts.add(s1); 41 ts.add(s2); 42 ts.add(s3); 43 ts.add(s4); 44 ts.add(s5); 45 ts.add(s6); 46 ts.add(s7); 47 ts.add(s8); 48 49 // 遍历 50 // 如果不重写自然排序方法,就会报错,因为没有给定如何排序,也没有保证唯一性 51 // java.lang.ClassCastException: com.wyh.treeSet.Student cannot be cast to 52 // java.lang.Comparable 53 for (Student2 s : ts) { 54 System.out.println(s.getName() + "---" + s.getAge()); 55 } 56 57 } 58 59 }
(4)案例:
A: 获取无重复的随机数(HashSet实现)
1 package com.wyh.HashSet_random; 2 3 import java.util.Arrays; 4 import java.util.HashSet; 5 import java.util.Random; 6 7 /** 8 * @author WYH 9 * @version 2019年11月17日 下午3:14:10 10 * 11 * 实现10个1-20的随机数 12 * 13 * 14 * 15 */ 16 public class HashSet_random_Demo { 17 public static void main(String[] args) { 18 //创建随机数对象 19 Random r = new Random(); 20 21 //创建HashSet集合 22 HashSet<Integer> hs = new HashSet<Integer>(); 23 24 //判断元素是否小于10 25 while(hs.size()<10) { 26 int num = r.nextInt(20)+1; 27 hs.add(num); 28 } 29 30 Object[] obj = hs.toArray(); 31 Arrays.sort(obj); 32 33 //遍历集合 34 for(Object i : obj) { 35 System.out.println(i); 36 } 37 } 38 }
B: 键盘录入学生按照总分从搞到底输出(TreeSet且用匿名内部类实现)
1 package TreeSet_Scanner; 2 3 import java.util.Comparator; 4 import java.util.Scanner; 5 import java.util.TreeSet; 6 7 8 /** 9 * @author WYH 10 * @version 2019年11月17日 下午3:25:25 11 * 12 * 键盘录入5个学生信息,并且按照总分高低排序 13 */ 14 public class TreeSetDemo { 15 public static void main(String[] args) { 16 //创建控制台输入对象 17 Scanner sc = new Scanner(System.in); 18 19 TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>(){ 20 @Override 21 public int compare(Student s1, Student s2) { 22 /*int num = s1.getName().length() - s2.getName().length(); 23 int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num; 24 int num3 = num2 == 0 ? s1.getSum() - s2.getSum() : num2; 25 return num3;*/ 26 int num = s2.getSum() - s1.getSum(); 27 int num2 = num == 0 ? s1.getMathNum() - s2.getMathNum() : num; 28 int num3 = num2 == 0 ? s1.getChinaNum() - s2.getChinaNum() : num2; 29 int num4 = num3 == 0 ? s1.getEngnishNum() - s2.getEngnishNum() : num3; 30 int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName()) : num4; 31 return num5; 32 } 33 }); 34 35 36 System.err.println("录入开始:"); 37 for(int i = 0 ; i < 5 ; i++) { 38 Student s = new Student(); 39 System.out.print("请输入第"+(i+1)+"个学生的姓名:"); 40 String name = sc.next(); 41 s.setName(name); 42 System.out.print("请输入该学生的数学成绩:"); 43 int mNum = sc.nextInt(); 44 s.setMathNum(mNum); 45 System.out.print("请输入该学生的语文成绩:"); 46 int cNum = sc.nextInt(); 47 s.setChinaNum(cNum); 48 System.out.print("请输入该学生的英语成绩:"); 49 int eNum = sc.nextInt(); 50 s.setEngnishNum(eNum); 51 52 int sum = mNum + cNum + eNum; 53 s.setSum(sum); 54 55 ts.add(s); 56 System.out.println(); 57 } 58 System.err.println("录入结束!"); 59 60 System.out.println("姓名 数学成绩 语文成绩 英语成绩 总分"); 61 for(Student s : ts) { 62 System.out.println(s.getName()+" "+s.getMathNum()+" "+s.getChinaNum()+" "+s.getEngnishNum() 63 +" "+s.getSum()); 64 } 65 66 67 } 68 69 }
2、Collection 集合体系 总结:
我们学完了Set集合,这个Collection体系下的另一个大模块,我们再来总结一下,把漏的LinkedList也总结一下:
Collection
| - - List 有序,可重复
| - - ArrayList
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高。
| - - Vector
底层数据结构是数组,查询快,增删慢。
线程安全,效率低。
| - - LinkedList
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高。
| - - Set 无序,唯一
| - - HashSet
底层数据结构是哈希表。
如何保证元素唯一性的呢?
依赖两个方法:hashCode()和equals()方法
开发中自动生成这两个方法即可。
| - - LinkedHashSet
底层数据结构是链表和哈希表
由链表保证元素有序
由哈希表保证元素唯一
人的类:重写方法
1 package com.wyh.linkedHashSet; 2 3 /** 4 * @author WYH 5 * @version 2019年11月17日 上午9:21:02 6 */ 7 public class Person { 8 private String name; 9 private int age; 10 11 public Person(String name, int age) { 12 super(); 13 this.name = name; 14 this.age = age; 15 } 16 public Person() { 17 super(); 18 // TODO Auto-generated constructor stub 19 } 20 21 public String getName() { 22 return name; 23 } 24 public void setName(String name) { 25 this.name = name; 26 } 27 public int getAge() { 28 return age; 29 } 30 public void setAge(int age) { 31 this.age = age; 32 } 33 34 //重写hashCode方法和equals方法 35 @Override 36 public int hashCode() { 37 final int prime = 31; 38 int result = 1; 39 result = prime * result + age; 40 result = prime * result + ((name == null) ? 0 : name.hashCode()); 41 return result; 42 } 43 @Override 44 public boolean equals(Object obj) { 45 if (this == obj) 46 return true; 47 if (obj == null) 48 return false; 49 if (getClass() != obj.getClass()) 50 return false; 51 Person other = (Person) obj; 52 if (age != other.age) 53 return false; 54 if (name == null) { 55 if (other.name != null) 56 return false; 57 } else if (!name.equals(other.name)) 58 return false; 59 return true; 60 } 61 62 63 }
测试类:
1 package com.wyh.linkedHashSet; 2 3 import java.util.LinkedHashSet; 4 5 /** 6 * @author WYH 7 * @version 2019年11月17日 上午9:40:25 8 * 9 * LinkedHashSet: 底层是由哈希表和链表实现的 10 * 11 * 哈希表确保元素的唯一性 12 * 链表确保元素有序(存储和取出一致 ) 13 */ 14 public class LinkedHashSetDemo1 { 15 public static void main(String[] args) { 16 LinkedHashSet<Person> lhs = new LinkedHashSet<Person>(); 17 //创建Person类 18 Person p1 = new Person("小虎",22); 19 Person p2 = new Person("小强",23); 20 Person p3 = new Person("小虎",22); 21 Person p4 = new Person("小美",21); 22 Person p5 = new Person("小雪",21); 23 24 lhs.add(p1); 25 lhs.add(p2); 26 lhs.add(p3); 27 lhs.add(p4); 28 lhs.add(p5); 29 30 // 31 for(Person p : lhs) { 32 System.out.println(p.getName()+"---"+p.getAge()); 33 } 34 35 } 36 37 }
| - - TreeSet
底层数据结构是红黑树。
如何保证元素的排序的呢?
自然排序
比较器排序
如何保证元素唯一性呢?
根据比较的返回值是否是0来决定
3、针对Collection集合我们到底使用谁呢?
唯一吗?
是:Set
排序吗?
是:TreeSet
否:HashSet
如果知道是Set,但是不知道是哪个Set,就用HashSet。
否:List
要安全吗?
是:vector
否:ArrayList 或者 LinkedList
查询多:ArrayList
增删多:LinkedList
如果知道是List,但是不知道是哪个List,就用ArrayList。
如果知道使用Collection集合,但是不知道使用谁,就用ArrayList。
如果知道是用集合,就用ArrayList.
4、在集合中常见的数据结构
ArrayXxx:底层数据结构是数组,查询快,增删慢。
LinkedXxx: 底层数据结构是链表,查询慢,增删快。
HashXxx: 底层数据结构是哈希表,依赖两个方法:hashCode() 和equals()方法
TreeXxx: 底层数据结构是二叉树,两种方式排序,自然排序和比较器排序。