【原】Java学习笔记028 - 集合
1 package cn.temptation; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 public class Sample01 { 7 public static void main(String[] args) { 8 /* 9 * 接口 Set<E>:一个不包含重复元素的 collection。 10 * 11 * 特点: 12 * 1、无序:指的是Set显示其元素的顺序 与 放入元素的顺序可能不一致 13 * 2、唯一:放入其中的元素是唯一的 14 */ 15 16 Set<String> set = new HashSet<>(); 17 18 set.add("China"); 19 set.add("USA"); 20 set.add("Japan"); 21 set.add("China"); 22 23 System.out.println("set:" + set); // set:[USA, China, Japan] 24 25 // 通过查看源码,发现会使用到元素的hashCode,hashCode相同的字符串添加时不重复添加 26 System.out.println("China".hashCode()); // 65078583 27 System.out.println("USA".hashCode()); // 84323 28 System.out.println("Japan".hashCode()); // 71341030 29 System.out.println((new String("China")).hashCode()); // 65078583 30 } 31 } 32 // 查看HashSet类的源码 33 //public boolean add(E e) { 34 // return map.put(e, PRESENT)==null; 35 //} 36 37 //private transient HashMap<E,Object> map; 38 39 // 查看HashMap类的源码 40 //public V put(K key, V value) { 41 // return putVal(hash(key), key, value, false, true); 42 //}
1 package cn.temptation; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 public class Sample02 { 7 public static void main(String[] args) { 8 // 类 HashSet<E>:此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。 9 // 它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。 10 11 Set<Student> set = new HashSet<>(); 12 13 set.add(new Student("张三", 18)); 14 set.add(new Student("李四", 20)); 15 set.add(new Student("王五", 22)); 16 set.add(new Student("张三", 18)); 17 18 // 分析:观察一下创建相同姓名、相同年龄的学生对象,它们的hashCode是否一致 19 // 原因在于因为Student类的对象没有重写hashCode方法和equals方法,默认使用Object类的hashCode方法和equals方法 20 21 System.out.println((new Student("张三", 18)).hashCode()); // 未重写时的值:118352462 重写后的值:776408 22 System.out.println((new Student("张三", 18)).hashCode()); // 未重写时的值:1550089733 重写后的值:776408 23 24 System.out.println("set:" + set); 25 } 26 }
1 package cn.temptation; 2 3 import java.util.LinkedHashSet; 4 import java.util.Set; 5 6 public class Sample03 { 7 public static void main(String[] args) { 8 /* 9 * 类 LinkedHashSet<E>:具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。 10 * 11 * 特点:有序唯一 12 * 1、唯一:通过哈希表来保证唯一 13 * 2、有序:通过链表来保证元素的有序 14 */ 15 Set<String> set = new LinkedHashSet<>(); 16 17 set.add("China"); 18 set.add("USA"); 19 set.add("Japan"); 20 set.add("China"); 21 set.add("France"); 22 set.add("England"); 23 24 System.out.println("set:" + set); 25 } 26 }
1 package cn.temptation; 2 3 import java.util.Set; 4 import java.util.TreeSet; 5 6 public class Sample04 { 7 public static void main(String[] args) { 8 /* 9 * 类 TreeSet<E>: 10 * 基于 TreeMap 的 NavigableSet 实现。 11 * 使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。 12 * 13 * 能够对其中的元素按照某种规则进行排序 14 * 15 * 某种规则: 16 * 1、可以按照自然顺序规则 17 * 2、可以按照自定义排序规则 18 */ 19 20 // 1、自然排序规则 21 Set<Integer> set1 = new TreeSet<>(); 22 23 set1.add(1); 24 set1.add(3); 25 set1.add(2); 26 set1.add(4); 27 28 System.out.println("treeset:" + set1); // treeset:[1, 2, 3, 4] 29 30 System.out.println("-------------------------------------------"); 31 32 Set<Character> set2 = new TreeSet<>(); 33 34 set2.add('j'); 35 set2.add('A'); 36 set2.add('v'); 37 set2.add('a'); 38 set2.add('G'); 39 set2.add('O'); 40 set2.add('o'); 41 set2.add('D'); 42 43 System.out.println("treeset:" + set2); // treeset:[A, D, G, O, a, j, o, v] 44 45 System.out.println("-------------------------------------------"); 46 47 Set<String> set3 = new TreeSet<>(); 48 49 set3.add("USA"); 50 set3.add("China"); 51 set3.add("Japan"); 52 set3.add("Chile"); 53 54 System.out.println("treeset:" + set3); // treeset:[Chile, China, Japan, USA] 55 56 System.out.println("-------------------------------------------"); 57 58 Set<String> set4 = new TreeSet<>(); 59 60 set4.add("张三"); 61 set4.add("李四"); 62 set4.add("王五"); 63 set4.add("张飞"); 64 65 System.out.println("treeset:" + set4); // treeset:[张三, 张飞, 李四, 王五] 显然不是根据拼音的顺序 66 67 System.out.println((int)'张' + " VS " + (int)'李' + " VS " + (int)'王'); // 24352 VS 26446 VS 29579 68 System.out.println((int)'三' + " VS " + (int)'飞'); // 19977 VS 39134 69 } 70 }
1 package cn.temptation; 2 3 import java.util.Set; 4 import java.util.TreeSet; 5 6 public class Sample05 { 7 public static void main(String[] args) { 8 // 可以按照自定义排序规则 9 10 // 思路: 11 // 考虑到Set容器中的元素是无序且唯一的,要让他们可以有顺序,其实就是让它们具备可比较的能力,即实现Comparable<T>接口 12 13 /* 14 * 接口 Comparable<T>:此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。 15 * 16 * Comparable<T>接口的常用成员方法: 17 * int compareTo(T o) :比较此对象与指定对象的顺序。 18 */ 19 20 Set<Student> set = new TreeSet<>(); 21 22 // 执行异常:java.lang.ClassCastException: cn.temptation.Student cannot be cast to java.lang.Comparable 23 set.add(new Student("张三", 20)); 24 set.add(new Student("李四", 18)); 25 set.add(new Student("王五", 22)); 26 set.add(new Student("张飞", 20)); 27 28 for (Student item : set) { 29 System.out.println(item); 30 } 31 } 32 }
1 package cn.temptation; 2 3 import java.util.Comparator; 4 import java.util.Set; 5 import java.util.TreeSet; 6 7 public class Sample06 { 8 public static void main(String[] args) { 9 /* 10 * 接口 Comparator<T>: 11 * 强行对某个对象 collection 进行整体排序 的比较函数。 12 * 可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。 13 * 还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。 14 */ 15 16 /* 17 * TreeSet类的构造函数: 18 * TreeSet(Comparator<? super E> comparator) :构造一个新的空 TreeSet,它根据指定比较器进行排序。 19 */ 20 21 // 创建比较器对象 22 Comparator<Student> comparator = new StudentCompartor(); 23 24 Set<Student> set = new TreeSet<>(comparator); 25 26 set.add(new Student("张三", 20)); 27 set.add(new Student("李四", 18)); 28 set.add(new Student("王五", 22)); 29 set.add(new Student("张飞", 20)); 30 31 for (Student item : set) { 32 System.out.println(item); 33 } 34 } 35 }
1 package cn.temptation; 2 3 import java.util.Comparator; 4 import java.util.Set; 5 import java.util.TreeSet; 6 7 public class Sample07 { 8 public static void main(String[] args) { 9 // 观察到TreeSet类的构造函数TreeSet(Comparator<? super E> comparator) :构造一个新的空 TreeSet,它根据指定比较器进行排序。 10 // 传入的是一个接口,所以考虑使用匿名内部类 11 // 匿名内部类写法的优点:使用了匿名内部类,就不用再制作实现Comparator接口的实现类了 12 13 Set<Student> set = new TreeSet<>(new Comparator<Student>() { 14 @Override 15 public int compare(Student student1, Student student2) { 16 // 首先按年龄的自然顺序 17 int resultAge = student1.getAge() - student2.getAge(); 18 19 /* 20 * 其次按姓名的自然顺序 21 * 字符串的比较时,考虑使用String类的compareTo方法 22 * String类的常用成员方法: 23 * int compareTo(String anotherString) :按字典顺序比较两个字符串。 24 */ 25 int result = (resultAge == 0) ? student1.getName().compareTo(student2.getName()) : resultAge; 26 27 return result; 28 } 29 }); 30 31 set.add(new Student("张三", 20)); 32 set.add(new Student("李四", 18)); 33 set.add(new Student("王五", 22)); 34 set.add(new Student("张飞", 20)); 35 36 for (Student item : set) { 37 System.out.println(item); 38 } 39 } 40 }
1 package cn.temptation; 2 3 import java.util.Set; 4 import java.util.TreeSet; 5 6 public class Sample08 { 7 public static void main(String[] args) { 8 Set<Integer> set = new TreeSet<>(); 9 10 set.add(1); 11 set.add(5); 12 set.add(4); 13 set.add(2); 14 set.add(3); 15 16 for (Integer item : set) { 17 System.out.println(item); 18 } 19 } 20 } 21 // 【TreeSet存储规则】 22 // 第一个元素存储时,直接作为根节点存储 23 // 从第二个元素开始,每个元素存储时从根节点开始比较 24 // 如果比根节点大,就作为根节点的右侧 25 // 如果比根节点小,就作为根节点的左侧 26 // 如果相等,就无视 27 // 【TreeSet取出规则】 28 // 取出时,根据(前序遍历、中序遍历、后序遍历) 29 // 从根节点开始,按照左、中、右的原则依次取出元素 30 31 // 查看TreeSet类的源码 32 //public boolean add(E e) { 33 // return m.put(e, PRESENT)==null; 34 //} 35 36 //private transient NavigableMap<E,Object> m; 37 38 //查看TreeMap类的源码 39 //public V put(K key, V value) { 40 // Entry<K,V> t = root; 41 // if (t == null) { 42 // compare(key, key); // type (and possibly null) check 43 // 44 // root = new Entry<>(key, value, null); 45 // size = 1; 46 // modCount++; 47 // return null; 48 // } 49 // int cmp; 50 // Entry<K,V> parent; 51 // // split comparator and comparable paths 52 // Comparator<? super K> cpr = comparator; 53 // if (cpr != null) { 54 // do { 55 // parent = t; 56 // cmp = cpr.compare(key, t.key); 57 // if (cmp < 0) 58 // t = t.left; 59 // else if (cmp > 0) 60 // t = t.right; 61 // else 62 // return t.setValue(value); 63 // } while (t != null); 64 // } 65 // else { 66 // if (key == null) 67 // throw new NullPointerException(); 68 // @SuppressWarnings("unchecked") 69 // Comparable<? super K> k = (Comparable<? super K>) key; 70 // do { 71 // parent = t; 72 // cmp = k.compareTo(t.key); 73 // if (cmp < 0) 74 // t = t.left; 75 // else if (cmp > 0) 76 // t = t.right; 77 // else 78 // return t.setValue(value); 79 // } while (t != null); 80 // } 81 // Entry<K,V> e = new Entry<>(key, value, parent); 82 // if (cmp < 0) 83 // parent.left = e; 84 // else 85 // parent.right = e; 86 // fixAfterInsertion(e); 87 // size++; 88 // modCount++; 89 // return null; 90 //}
1 package cn.temptation; 2 3 // 注意:只定义Student类,放入到TreeSet集合中会产生执行异常:java.lang.ClassCastException: cn.temptation.Student cannot be cast to java.lang.Comparable 4 //public class Student { 5 public class Student implements Comparable<Student> { 6 // 成员变量 7 private String name; 8 private int age; 9 10 // 构造函数 11 public Student() { 12 super(); 13 } 14 15 public Student(String name, int age) { 16 super(); 17 this.name = name; 18 this.age = age; 19 } 20 21 // 成员方法 22 public String getName() { 23 return name; 24 } 25 26 public void setName(String name) { 27 this.name = name; 28 } 29 30 public int getAge() { 31 return age; 32 } 33 34 public void setAge(int age) { 35 this.age = age; 36 } 37 38 // 为了去除重复内容的对象,需要重写hashCode方法和equals方法 39 @Override 40 public int hashCode() { 41 final int prime = 31; 42 int result = 1; 43 result = prime * result + age; 44 result = prime * result + ((name == null) ? 0 : name.hashCode()); 45 return result; 46 } 47 48 @Override 49 public boolean equals(Object obj) { 50 if (this == obj) 51 return true; 52 if (obj == null) 53 return false; 54 if (getClass() != obj.getClass()) 55 return false; 56 Student other = (Student) obj; 57 if (age != other.age) 58 return false; 59 if (name == null) { 60 if (other.name != null) 61 return false; 62 } else if (!name.equals(other.name)) 63 return false; 64 return true; 65 } 66 67 @Override 68 public String toString() { 69 return "学生 [姓名为:" + name + ", 年龄为:" + age + "]"; 70 } 71 72 // 需要重写compareTo方法进行比较 73 // 制定比较的规则:先比较年龄的大小,年龄相同时比较姓名的顺序 74 @Override 75 public int compareTo(Student anotherStudent) { 76 // 首先按年龄的自然顺序 77 int resultAge = this.age - anotherStudent.age; 78 79 /* 80 * 其次按姓名的自然顺序 81 * 字符串的比较时,考虑使用String类的compareTo方法 82 * String类的常用成员方法: 83 * int compareTo(String anotherString) :按字典顺序比较两个字符串。 84 */ 85 int result = (resultAge == 0) ? this.name.compareTo(anotherStudent.name) : resultAge; 86 87 return result; 88 } 89 }
1 package cn.temptation; 2 3 import java.util.Comparator; 4 5 /** 6 * 实现比较器的类 7 * 这里没有在Student类上直接实现Comparator<T>接口,是因为Comparator<T>接口的compare方法比较的是两个对象 8 * 9 * Comparator<T>接口的常用成员方法: 10 * 1、int compare(T o1, T o2) :比较用来排序的两个参数。 11 * 2、boolean equals(Object obj) :指示某个其他对象是否“等于”此 Comparator。 12 * 13 * 注意:因为Object基类就有equals方法,所以不显式进行重写,就是使用Object基类的equals方法,不会提示语法出错 14 */ 15 public class StudentCompartor implements Comparator<Student> { 16 /* 17 * 重写进行比较的方法 18 * 制定比较的规则:先比较年龄的大小,年龄相同时比较姓名的顺序 19 */ 20 @Override 21 public int compare(Student student1, Student student2) { 22 // 首先按年龄的自然顺序 23 int resultAge = student1.getAge() - student2.getAge(); 24 25 /* 26 * 其次按姓名的自然顺序 27 * 字符串的比较时,考虑使用String类的compareTo方法 28 * String类的常用成员方法: 29 * int compareTo(String anotherString) :按字典顺序比较两个字符串。 30 */ 31 int result = (resultAge == 0) ? student1.getName().compareTo(student2.getName()) : resultAge; 32 33 return result; 34 } 35 }