JavaSE学习总结第17天_集合框架3

17.01 ArrayList集合的toString()方法源码解析

代码:

Collection c = new ArrayList();

c.add("hello");

c.add("world");

c.add("java");

 

System.out.println(c);

  

输出c时默认调用的是c的toString()方法

A:Collection c = new ArrayList();

这是多态,所以输出c的 toString()方法,其实是输出ArrayList的toString()方法

B:看 ArrayList 的 toString()方法

在ArrayList里面却没有发现toString()。应该去父类查找→ AbstractList → AbstractCollection

C:toString()的方法源码

复制代码
 1 public String toString() 
 2 {
 3     Iterator<E> it = iterator(); //集合本身调用迭代器方法,得到集合迭代器
 4     if (! it.hasNext())
 5         return "[]";
 6 
 7     StringBuilder sb = new StringBuilder();
 8     sb.append('[');
 9     for (;;) 
10     {
11         E e = it.next(); //e=hello,world,java
12         sb.append(e == this ? "(this Collection)" : e);
13         if (! it.hasNext())
14             //[hello, world, java]
15             return sb.append(']').toString();
16         sb.append(',').append(' ');
17     }
18 }
复制代码

17.02 Set集合概述及特点

Set接口概述:一个不包含重复元素的 collection

特点:

无序(存入与取出的顺序不一致)

唯一(存入集合的元素唯一)

17.03 HashSet存储字符串并遍历

HashSet类概述:不保证 set 的迭代顺序,特别是它不保证该顺序恒久不变。此类允许使用 null 元素。

例:

复制代码
 1 public class Practice 
 2 {
 3     public static void main(String[] args)
 4     {
 5         HashSet<String> hs = new HashSet<String>();
 6         hs.add("hello");
 7         hs.add("world");
 8         hs.add("world");
 9         hs.add("java");
10         
11         for (String s : hs) 
12         {
13             System.out.println(s);
14         }
15     }
16 }
复制代码

运行结果:

hello
java
world

17.04 HashSet保证元素唯一性的源码解析

复制代码
 1 interface Collection
 2 {...}
 3 
 4 interface Set extends Collection 
 5 {...}
 6 
 7 class HashSet implements Set 
 8 {
 9     private static final Object PRESENT = new Object();
10     private transient HashMap<E,Object> map;
11     
12     public HashSet() 
13     {
14         map = new HashMap<>();
15     }
16     
17     public boolean add(E e) 
18     { //e=hello,world
19         return map.put(e, PRESENT)==null;
20     }
21 }
22 
23 class HashMap implements Map 
24 {
25     public V put(K key, V value) 
26     { //key=e=hello,world
27     
28         //看哈希表是否为空,如果空,就开辟空间
29         if (table == EMPTY_TABLE) 
30         {
31             inflateTable(threshold);
32         }
33         
34         //判断对象是否为null
35         if (key == null)
36             return putForNullKey(value);
37         
38         int hash = hash(key); //和对象的hashCode()方法相关
39         
40         //在哈希表中查找hash值
41         int i = indexFor(hash, table.length);
42         for (Entry<K,V> e = table[i]; e != null; e = e.next) 
43         {
44             //这次的e其实是第一次的world
45             Object k;
46             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) 
47             {
48                 V oldValue = e.value;
49                 e.value = value;
50                 e.recordAccess(this);
51                 return oldValue;
52                 //走这里其实是没有添加元素
53             }
54         }
55 
56         modCount++;
57         addEntry(hash, key, value, i); //把元素添加
58         return null;
59     }
60     
61     transient int hashSeed = 0;
62     
63     final int hash(Object k) 
64     { //k=key=e=hello,
65         int h = hashSeed;
66         if (0 != h && k instanceof String) 
67         {
68             return sun.misc.Hashing.stringHash32((String) k);
69         }
70 
71         h ^= k.hashCode(); //这里调用的是对象的hashCode()方法
72 
73         // This function ensures that hashCodes that differ only by
74         // constant multiples at each bit position have a bounded
75         // number of collisions (approximately 8 at default load factor).
76         h ^= (h >>> 20) ^ (h >>> 12);
77         return h ^ (h >>> 7) ^ (h >>> 4);
78     }
79 }
复制代码

通过查看add方法的源码,知道这个方法底层依赖两个方法:hashCode()和equals()。

判断元素唯一性的方式:通过对象的hashCode和equals方法来完成元素唯一性

如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中。

如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true。

如果为true,视为相同元素,不存。如果为false,那么视为不同元素,就进行存储。

 

如果类没有重写这两个方法,默认使用的Object()。一般来说不会相同。

17.05 HashSet存储自定义对象并遍历

复制代码
 1 public class Practice 
 2 {
 3     public static void main(String[] args)
 4     {
 5         HashSet<Student> hs = new HashSet<Student>();
 6         
 7         hs.add(new Student("小明",23));
 8         hs.add(new Student("旺财",12));
 9         hs.add(new Student("旺财",12));
10         hs.add(new Student("小强",24));
11         hs.add(new Student("小明",22));
12         hs.add(new Student("小红",22));
13         
14         for(Student s : hs)
15         {
16             System.out.println(s.getName()+":"+s.getAge());
17         }
18     }
19 }
复制代码

 17.06 HashSet保证元素唯一性的代码体现

上例中重复元素被存入到了集合中,因为Student没有重写hashCode和equals方法,默认使用的Object()的hashCode和equals方法,一般来说结果不会相同,所以存入到了集合中,Student类应重写hashCode和equals方法(自动生成)。

复制代码
 1    @Override
 2     public int hashCode() 
 3     {
 4         final int prime = 31;
 5         int result = 1;
 6         result = prime * result + age;
 7         result = prime * result + ((name == null) ? 0 : name.hashCode());
 8         return result;
 9     }
10 
11     @Override
12     public boolean equals(Object obj) 
13     {
14         if (this == obj)
15             return true;
16         if (obj == null)
17             return false;
18         if (getClass() != obj.getClass())
19             return false;
20         Student other = (Student) obj;
21         if (age != other.age)
22             return false;
23         if (name == null) 
24         {
25             if (other.name != null)
26                 return false;
27         } else if (!name.equals(other.name))
28             return false;
29         return true;
30     }
复制代码

 17.07 LinkedHashSet的概述和使用

LinkedHashSet类概述:

元素有序唯一:由链表保证元素有序、由哈希表保证元素唯一

例:

复制代码
 1 public class Practice 
 2 {
 3     public static void main(String[] args)
 4     {
 5         LinkedHashSet<String> hs = new LinkedHashSet<String>();
 6         hs.add("hello");
 7         hs.add("world");
 8         hs.add("world");
 9         hs.add("java");
10         
11         for(String s : hs)
12         {
13             System.out.println(s);
14         }
15     }
16 }
复制代码

 运行结果:

hello
world
java

17.08 TreeSet存储Integer类型的元素并遍历

TreeSet类概述:使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。

例:

复制代码
 1 public class Practice 
 2 {
 3     public static void main(String[] args)
 4     {
 5         TreeSet<Integer> ts = new TreeSet<Integer>();
 6         ts.add(20);
 7         ts.add(18);
 8         ts.add(23);
 9         ts.add(22);
10         ts.add(17);
11         ts.add(24);
12         ts.add(19);
13         ts.add(18);
14         
15         for(Integer i : ts)
16         {
17             System.out.print(i+" ");
18         }
19     }
20 }
复制代码

运行结果:

17 18 19 20 22 23 24 

17.09 TreeSet保证元素排序的源码解析

复制代码
 1 interface Collection {...}
 2 
 3 interface Set extends Collection {...}
 4 
 5 interface NavigableMap {}
 6 
 7 class TreeMap implements NavigableMap 
 8 {
 9      public V put(K key, V value) 
10     {
11         Entry<K,V> t = root;
12         if (t == null) 
13         {
14             compare(key, key); // type (and possibly null) check
15 
16             root = new Entry<>(key, value, null);
17             size = 1;
18             modCount++;
19             return null;
20         }
21         int cmp;
22         Entry<K,V> parent;
23         // split comparator and comparable paths
24         Comparator<? super K> cpr = comparator;
25         if (cpr != null) 
26         {
27             do 
28             {
29                 parent = t;
30                 cmp = cpr.compare(key, t.key);
31                 if (cmp < 0)
32                     t = t.left;
33                 else if (cmp > 0)
34                     t = t.right;
35                 else
36                     return t.setValue(value);
37             } while (t != null);
38         }
39         else 
40         {
41             if (key == null)
42                 throw new NullPointerException();
43             Comparable<? super K> k = (Comparable<? super K>) key;
44          do 
45             {
46                 parent = t;
47                 cmp = k.compareTo(t.key);
48                 if (cmp < 0)
49                     t = t.left;
50                 else if (cmp > 0)
51                     t = t.right;
52                 else
53                     return t.setValue(value);
54             } while (t != null);
55         }
56         Entry<K,V> e = new Entry<>(key, value, parent);
57         if (cmp < 0)
58             parent.left = e;
59         else
60             parent.right = e;
61         fixAfterInsertion(e);
62         size++;
63         modCount++;
64         return null;
65     }
66 }
67 
68 class TreeSet implements Set 
69 {
70     private transient NavigableMap<E,Object> m;
71     
72     public TreeSet() 
73     {
74          this(new TreeMap<E,Object>());
75     }
76 
77     public boolean add(E e) 
78     {
79         return m.put(e, PRESENT)==null;
80     }
81 }
复制代码

真正的比较是依赖于元素的compareTo()方法,而这个方法是定义在 Comparable里面的。

所以,要想重写该方法,就必须是先实现 Comparable接口。这个接口表示的就是自然排序。

17.10 TreeSet保证元素唯一性和自然排序的原理和图解

 

17.11 TreeSet存储自定义对象并遍历练习1

Student类实现自然排序接口Comparable,重写compareTo()方法

复制代码
1 @Override
2 public int compareTo(Student s) 
3 {
4     //主要条件,按年龄排
5     int num = this.age - s.age;
6     //次要条件,年龄相同按姓名排
7     int num2 = (num == 0)?this.name.compareTo(s.name):num;
8     return num2;
9 }
复制代码

17.12 TreeSet存储自定义对象并遍历练习2

Student类实现自然排序接口Comparable,重写compareTo()方法

复制代码
 1 @Override
 2 public int compareTo(Student s) 
 3 {
 4     // 主要条件 姓名的长度
 5     int num = this.name.length() - s.name.length();
 6     // 姓名的长度相同,比较姓名的内容是否相同
 7     int num2 = num == 0 ? this.name.compareTo(s.name) : num;
 8     // 姓名的长度和内容相同,比较年龄是否相同,继续判断年龄
 9     int num3 = num2 == 0 ? this.age - s.age : num2;
10     return num3;
11 }
复制代码

17.13 TreeSet保证元素唯一性和比较器排序的原理及代码实现

复制代码
 1 // 比较器排序,让集合具备比较性,匿名内部类实现
 2 TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() 
 3 {
 4     @Override
 5     public int compare(Student s1, Student s2) 
 6     {
 7         // 姓名长度
 8         int num = s1.getName().length() - s2.getName().length();
 9         // 姓名内容
10         int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
11         // 年龄
12         int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;
13         return num3;
14     }
15     
16 });
复制代码

17.14 TreeSet对元素排序的总结

唯一性:根据比较的返回的是否是0来决定

排序: 1.自然排序,一个类的元素想要进行自然排序就必须实现自然排序接口Comparable(元素具备比较性)

     2.比较器排序,让集合的构造方法接收一个比较器接口的子类对象Comparator(集合具备比较性)

17.15 产生10个1-20之间的随机数要求随机数不能重复案例简洁版

编写一个程序,获取10个1至20的随机数,要求随机数不能重复。

复制代码
 1 public class Practice 
 2 {
 3     public static void main(String[] args)
 4     {
 5         // 创建随机数对象
 6         Random r = new Random();
 7 
 8         // 创建一个Set集合
 9         HashSet<Integer> ts = new HashSet<Integer>();
10 
11         // 判断集合的长度是不是小于10
12         while (ts.size() < 10) 
13         {
14             int num = r.nextInt(20) + 1;
15             ts.add(num);
16         }
17 
18         // 遍历Set集合
19         for (Integer i : ts) 
20         {
21             System.out.println(i);
22         }
23     }
24 }
复制代码

17.16 键盘录入学生信息按照总分排序后输出在控制台案例

Student类

复制代码
 1 public class Student
 2 {
 3     private String name;
 4     private int chinese;
 5     private int math;
 6     private int english;
 7     public Student(String name, int chinese, int math, int english) 
 8     {
 9         super();
10         this.name = name;
11         this.chinese = chinese;
12         this.math = math;
13         this.english = english;
14     }
15     public String getName() 
16     {
17         return name;
18     }
19     public void setName(String name) 
20     {
21         this.name = name;
22     }
23     public int getChinese() 
24     {
25         return chinese;
26     }
27     public void setChinese(int chinese) 
28     {
29         this.chinese = chinese;
30     }
31     public int getMath() 
32     {
33         return math;
34     }
35     public void setMath(int math) 
36     {
37         this.math = math;
38     }
39     public int getEnglish() 
40     {
41         return english;
42     }
43     public void setEnglish(int english) 
44     {
45         this.english = english;
46     }
47     
48     public int getSum()
49     {
50         return this.chinese+this.english+this.math;
51     }
52 }
复制代码

测试类

复制代码
 1 public class Practice 
 2 {
 3     public static void main(String[] args)
 4     {
 5         TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() 
 6         {
 7             @Override
 8             public int compare(Student s1, Student s2) 
 9             {
10                 //按总分比较
11                 int num1 = s2.getSum() - s1.getSum();
12                 //总分相同按语文成绩比较
13                 int num2 = num1==0?s1.getChinese() - s2.getChinese():num1;
14                 //语文成绩相同按数学成绩比较
15                 int num3 = num2==0?s1.getMath() - s2.getMath():num2;
16                 //数学成绩相同按英语成绩比较
17                 int num4 = num3==0?s1.getChinese() - s2.getChinese():num3;
18                 //英语成绩相同按姓名比较
19                 int num5 = num4==0?s1.getName().compareTo(s2.getName()):num4;
20                 return num5;
21             }
22         });
23         for (int i = 1; i <= 5; i++) 
24         {
25             Scanner sc = new Scanner(System.in);
26             System.out.println("请输入第"+i+"位学生的姓名");
27             String name = sc.nextLine();
28             System.out.println("请输入第"+i+"位学生的语文成绩");
29             String chinese = sc.nextLine();
30             System.out.println("请输入第"+i+"位学生的数学成绩");
31             String math = sc.nextLine();
32             System.out.println("请输入第"+i+"位学生的英语成绩");
33             String english = sc.nextLine();
34             
35             Student s = new Student(name, Integer.parseInt(chinese), Integer.parseInt(math), Integer.parseInt(english));
36             ts.add(s);
37         }
38         System.out.println("学生信息如下");
39         System.out.println("姓名	语文	数学	英语	总分");
40         for(Student s:ts)
41         {
42             System.out.println(s.getName()+"	"+s.getChinese()+"	"+s.getMath()+"	"+s.getEnglish()+"	"+s.getSum());
43         }
44     }
45 }
复制代码

运行结果:

原文地址:https://www.cnblogs.com/hoop-superman/p/5495713.html