tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
/*
8、通过前面比较如果执行到这里,说明该存储位置有元素,那么就比较存入的元素和以前的元素的哈希值
这是一个短路运算符,如果前面为false,那么后面不会执行
8.1、如果这两个哈希值不同,那么返回false,if不成立,继续向下执行,把元素添加到集合
8.2、如果哈希值相同,那么会执行(k = p.key) == key || (key != null && key.equals(k)))
首先进行对象的比较(比较地址),如果两个地址值相同,那么说明这两个是同一个元素。
如果地址值不相同,那就调用equal方法,判断这两个元素的内容是否一样(两个元素哈希值一样),如果一样则 不存入集合,如果不一样,那么则把元素添加到集合中
*/
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//这个else if先不用看
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
简单概括来说就是以下几步
1. 先如果两个元素存储位置不相同,那么直接把元素添加到集合即可
2. 如果两个元素存储位置相同,那么比较两个元素的哈希值
3. 如果两个元素的哈希值不一样,那么存储进去
4. 如果两个元素的哈希值一样,那么比较两个元素的内容
5. 如果两个元素的内容一样,那么不添加进集合
6. 如果两个元素的内容不一样,那么不用添加进集合
HashSet集合之所以可以一个位置存储多个元素,那是因为HashSet集合整体是数组结构,而每个位置上是链式结构,所以一个位置能存储多个元素
* * *
[]( )四、哈希值
------------------------------------------------------------------------
哈希值:是JDK根据**对象地址**或者**字符串**或者**数字**算出来的int类型的数值
因此在Object类中有一个方法可以获取**对象的哈希值**
* public int hashCode() :返回对象的哈希码值
哈希值的特点在代码中体现
package CodeDemo;
/*
学生对象类
*/
public class Student {
private int age;
private String name;
public Student(){}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package CodeDemo;
public class CodeDemo {
public static void main(String[] args) {
Student s1 = new Student(18, "刘德华");
Student s2 = new Student(18, "刘德华");
Student s3 = new Student(20, "周星驰");
//同一对象多次调用hashCode()方法返回的哈希值是相同的
System.out.println(s1.hashCode());
System.out.println(s1.hashCode());
System.out.println("----------------------");
//默认情况下,不同对象的哈希值是不同的
//可以通过方法重写,可以实现不同对象的哈希值相同
System.out.println(s2.hashCode());
System.out.println(s3.hashCode());
System.out.println("----------------------");
//不同的字符串对应的哈希值也是不同的
System.out.println("Hello".hashCode());
System.out.println("World".hashCode());
System.out.println("----------------------");
//但相同的字符串对应的哈希值是相同的
System.out.println("JAVA".hashCode());
System.out.println("JAVA".hashCode());
System.out.println("----------------------");
//不同汉字的哈希值可能一样
System.out.println("重地".hashCode());
System.out.println("通话".hashCode());
System.out.println("中秋".hashCode());
}
}
运行结果
149928006
149928006
713338599
168423058
69609650
83766130
2269730
2269730
1179395
1179395
651582
* * *
[]( )五、HashSet学生实例应用
----------------------------------------------------------------------------------
> 需求:创建一个存储学生对象的集合,存储多个学生对象,使用程序遍历
>
> 要求:学生对象的成员变量值相同,认为是同一个对象
**思路**
1. 定义学生类
2. 创建HashSet集合对象
3. 创建学生对象
4. 把学生添加到集合
5. 遍历集合(增强for)
6. 在学生类中重写两个方法(hashCode()和equals())
**代码实现**
package CodeDemo;
/*
学生类
*/
import java.util.Objects;
public class Student {
private int age;
private String name;
public Student(){}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
}
package CodeDemo;
/*
测试类
*/
import java.util.HashSet;
import java.util.Iterator;
public class HashDemo {
public static void main(String[] args) {
Student s1 = new Student(19, "成龙");
Student s2 = new Student(20, "刘德华");
Student s3 = new Student(20, "刘德华");
HashSet<Student> hashSet = new HashSet<Student>();
hashSet.add(s1);
hashSet.add(s2);
hashSet.add(s1);
hashSet.add(s3);
for (Student s : hashSet){
System.out.println(s);
}
Iterator<Student> iterator = hashSet.iterator();
while (iterator.hasNext()){
Student s = iterator.next();
System.out.println(s);
}
}
}
**运行结果**
Student{age=19, name=‘成龙’}
Student{age=20, name=‘刘德华’}
Student{age=19, name=‘成龙’}
Student{age=20, name=‘刘德华’}
为什么要重写hashCode方法呢???
那是因为hashcode重写的好还可以提升hashset的效率
[]( )六、LinkedHashSet集合概述和特点
-----------------------------------------------------------------------------------------
特点
1. 哈希表和链式实现的Set接口,具有可预测的迭代次序
2. 由链式保证元素有序,也就是说元素的存储和取出顺序是一致的
3. 由哈希表保证元素唯一,也就是说没有重复的元素
package CodeDemo;
import java.util.LinkedHashSet;
public class LinkDemo {
public static void main(String[] args) {
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("Hello");
linkedHashSet.add("World");
linkedHashSet.add("java");
linkedHashSet.add("Hello");
for (String s : linkedHashSet){
System.out.println(s);
}
}
}
Hello
World
java
[]( )七、TreeSet集合概述和特点
-----------------------------------------------------------------------------------
TreeSet集合特点
> TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序排列
1. 元素有序,但是这里的顺序值得不是存储和取出顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
TreeSet():根据其元素的自然排序进行排序(自然排序是指ABCD这样的顺序)
TreeSet(Comparator comparator):根据指定的比较器进行排序
2. 没有带索引的方法,所以不能使用普通的for循环遍历
3. 由于是Set集合,所以不包含重复元素的集合
package CodeDemo;
import java.util.TreeSet;
public class TreeDemo {
public static void main(String[] args) {
//创建集合对象
//集合存储的是对象(引用类型),所以存储整数应该存储Inerger
TreeSet<Integer> tree = new TreeSet<Integer>();
//添加数据
tree.add(10);
tree.add(100);
tree.add(1000);
tree.add(10);
tree.add(1);
for (int num : tree){
System.out.println(num);
}
}
}
输出结果
1
10
100
1000
由结果可见,输出的数据没有重复的数据
并且输入的顺序是10、100、1000、1,但是输出的顺序是1、10、100、1000,所以可证明TreeSet( )根据其元素的自然排序进行排序.
[]( )八、自然排序Comparable的使用
--------------------------------------------------------------------------------------
从例子中体会其作用
* 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
* 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
package CodeDemo;
/*
学生类
*/
import java.util.Objects;
public class Student implements Comparable {
private int age;
private String name;
public Student(){}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
@Override
public int compareTo(Student o) {
//return 0; 表示相同
//return 1; 表示从小到大排序
//return -1; 表示从大到小排序
//按照年龄从小到大排序
int num = this.age - o.age;
//年龄相同时候,按照姓名字母顺序排序
int num2 = num == 0? this.name.compareTo(o.name) : num;
return num2;
}
}
package CodeDemo;
import java.util.TreeSet;
public class ComparableDemo {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> treeSet = new TreeSet<Student>();
//创建学生对象
Student s1 = new Student(19, "小明");
Student s2 = new Student(20, "小红");
Student s3 = new Student(20, "小白");
//添加到集合中
treeSet.add(s1);
treeSet.add(s2);
treeSet.add(s3);
treeSet.add(s1);
for (Student s : treeSet){
System.out.println(s);
}
}
}
n’n’n
运行结果
Student{age=19, name=‘小明’}
Student{age=20, name=‘小白’}
Student{age=20, name=‘小红’}
结论
* 用TreeSet集合存储自定义对象,无参构造方法使用的是**自然排序**对元素进行排序
* 自然排序,就是**让元素所有的类实现Comparable接口**,重写compareTo(T o)方法
* 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
[]( )九、比较器排序Comparator
------------------------------------------------------------------------------------
| `TreeSet(Comparator<? super E> comparator)` | 构造一个新的,空的树集,根据指定的比较器进行排序。 |
| --- | --- |
_**为了方便,这里可以运用匿名内部类的知识去解决**_
package TEXT;
/*
学生类
*/
public class Student {
private String name;
private int age;
public Student(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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package TEXT;
import java.util.Comparator;
import java.util.TreeSet;
public class Demo {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> treeSet = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int num = o1.getAge() - o2.getAge();
int num2 = num == 0? o1.getName().compareTo(o2.getName()) : num;
return num2;
}
});
//创建学生对象
Student s1 = new Student("小明", 19);
Student s2 = new Student("小红", 20);
Student s3 = new Student("小白", 20);
//添加到集合中