day16.Map集合
课前回顾:
1.LinkedList:
特点:有序 元素可重复 有索引
数据结构:双向链表
方法:有很多直接操作首尾元素的方法
2.增强for:遍历集合或者数组->foreach
格式:
for(元素类型 变量名:集合名或者数组名){
变量名就代表的是每一个元素
}
注意:
使用增强for的时候,不要随意改变集合长度
当用增强for遍历集合时,实现原理是迭代器
当用增强for遍历数组时,实现原理就是普通的for
3.集合工具类:Collections
方法:
shuffle(集合)->打乱元素顺序
sort(集合)->排序
sort(集合,比较器)->按照指定的规则排序
4.比较器:
Comparable
Comparator
5.泛型:
定义类: public class 类名<E>{}
定义方法: 修饰符 <E> 返回值类型 方法名(E e){}
定义接口: public interface 接口名<E>{}
6.Set接口
Set接口中的方法没有对Collection中的方法进行扩充
7.HashSet
特点:
无序
无索引
元素不可重复
数据结构:哈希表
jdk8之前:哈希表 = 数组+链表
jdk8之后:哈希表 = 数组+链表+红黑树
加入红黑树原因:查找快
方法:底层都是依靠HashMap实现的
8.LinkedHashSet:
特点:
有序
无索引
元素不可重复
数据结构:链表+哈希表
9.哈希值:需要调用hashCode方法计算出来的一个十进制数
内容不一样,哈希值有可能一样
内容一样,哈希值一定一样的
10.HashSet存储元素的过程(和今天要学的HashMap是一样的)
先比较元素的哈希值,再比较元素的内容
如果哈希值不一样,直接存
如果哈希值一样,再比较内容
如果内容不一样,也存
如果内容一样,哈希值也一样,后面的把前面的覆盖掉
11.注意:存储的时候比较的哈希值(是内容的哈希值) 内容(就是内容)
所以,存储到set集合中保证元素唯一的话,需要重写hashCode和equals方法
今日重点:
1.Map的全部
2.知道HashMap和HashTable的区别
3.会使用Properties集合
4.会集合嵌套
5.了解哈希表的存储过程和细节
6.会操作TreeSet和TreeMap
第一章.Map集合
1.Map的介绍
1.概述:
双列集合的接口
2.特点:
元素都是key value形式(键值对)存储 ->一个key 对应一个value
key唯一 (将key去重复,过程和HashSet一样)
无序
没有索引
3.元素形式:
map.put("文章","马伊琍")
map.put("文章","姚笛")->和上面的key重复,会将上面的键值对覆盖
2.HashMap的介绍以及使用
1.HashMap 实现了 Map接口
2.特点:
元素都是key value形式(键值对)存储 ->一个key 对应一个value
key唯一 (将key去重复,过程和HashSet一样)
无序
没有索引
3.数据结构:哈希表
jdk8之前:哈希表 = 数组+链表
jdk8之后:哈希表 = 数组+链表+红黑树
4.方法:
- public V put(K key, V value): 把指定的键与指定的值添加到Map集合中。
- public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
- public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
- public Set<K> keySet(): 获取Map集合中所有的键,存储到Set集合中。
- public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。
- public boolean containsKey(Object key):判断该集合中是否有此键。
- public Collection<V> values() 返回Map集合中的所有值到Collection集合。
public class Test01 {
public static void main(String[] args) {
//创建HashMap集合
HashMap<String, String> hashMap = new HashMap<>();
//- public V put(K key, V value): 把指定的键与指定的值添加到Map集合中。
hashMap.put("郭靖","黄蓉");
hashMap.put("杨过","小龙女");
hashMap.put("张无忌","赵敏");
hashMap.put("张翠山","殷素素");
hashMap.put("张无忌","周芷若");
System.out.println(hashMap);
//- public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。
/* String value1 = hashMap.remove("郭靖");
System.out.println(value1);
System.out.println(hashMap);*/
//- public V get(Object key) 根据指定的键,在Map集合中获取对应的值。
String value2 = hashMap.get("杨过");
System.out.println(value2);
//- public boolean containsKey(Object key):判断该集合中是否有此键。
boolean result01 = hashMap.containsKey("杨过");
System.out.println(result01);
//- public Collection<V> values() 返回Map集合中的所有值到Collection集合。
Collection<String> collection = hashMap.values();
System.out.println(collection);
}
}
3.HashMap的两种遍历方式
3.1方式一:获取key,然后根据key获取值
- public Set<K> keySet(): 获取Map集合中所有的键,存储到Set集合中。
public class Test02 {
public static void main(String[] args) {
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("郭靖","黄蓉");
hashMap.put("杨过","小龙女");
hashMap.put("张无忌","赵敏");
hashMap.put("张翠山","殷素素");
hashMap.put("涛哥","柳岩");
/*
将map中的所有key获取出来,存到set集合中
*/
Set<String> set = hashMap.keySet();
/*
遍历set集合,将key都遍历出来
然后调用map中的get(key)->根据key获取对应的value
*/
for (String key : set) {
String value = hashMap.get(key);
System.out.println(key+"..."+value);
}
}
}
3.2方式二:同时获取key和value
- public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象,放到set集合中(Set集合)。
public class Test03 {
public static void main(String[] args) {
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("郭靖","黄蓉");
hashMap.put("杨过","小龙女");
hashMap.put("张无忌","赵敏");
hashMap.put("张翠山","殷素素");
hashMap.put("涛哥","柳岩");
/*
将记录着键值对的Map.Entry对象获取出来,放到set集合中
*/
Set<Map.Entry<String, String>> set = hashMap.entrySet();
/*
将Map.Entry从set中遍历出来
遍历出来之后,就可以用getKey getValue
*/
for (Map.Entry<String, String> entry : set) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"="+value);
}
}
}
5.HashMap存储自定义对象
1.如何保证key唯一呢?
重写hashCode和equals方法
public class Person {
private String name;
private int age;
public Person() {
}
public Person(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 "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class Test04 {
public static void main(String[] args) {
HashMap<Person, String> hashMap = new HashMap<>();
hashMap.put(new Person("涛哥",18),"廊坊");
hashMap.put(new Person("柳岩",36),"湖南");
hashMap.put(new Person("涛哥",18),"北京");
System.out.println(hashMap);
}
}
6.Map的练习
需求:利用HashMap集合统计字符串中字符出现的次数
步骤:
1.创建HashMap集合 key为String value为Integer
key代表字符 value代表字符在字符串中出现的次数
2.遍历字符串,将每一个字符都获取出来
3.在遍历的过程中,拿着字符去判断,判断map集合中是否包含遍历出来的字符
4.如果不包含,直接将字符存进去,value存为1
5.如果包含,将字符对应的value获取出来,对value进行加1,再将字符和加完之后的value重新存到map中
6.输出
public class Test05 {
public static void main(String[] args) {
/*1.创建HashMap集合 key为String value为Integer
key代表字符 value代表字符在字符串中出现的次数*/
HashMap<String, Integer> hashMap = new HashMap<>();
//2.遍历字符串,将每一个字符都获取出来
Scanner sc = new Scanner(System.in);
System.out.println("请你输入一个字符串:");
String data = sc.next();
char[] chars = data.toCharArray();
for (char aChar : chars) {
String key = aChar+"";
//3.在遍历的过程中,拿着字符去判断,判断map集合中是否包含遍历出来的字符
if (!hashMap.containsKey(key)){
//4.如果不包含,直接将字符存进去,value存为1
hashMap.put(key,1);
}else{
//5.如果包含,将字符对应的value获取出来,对value进行加1,再将字符和加完之后的value重新存到map中
Integer value = hashMap.get(key);
value++;
hashMap.put(key,value);
}
}
//6.输出
System.out.println(hashMap);
}
}
7.LinkedHashMap
1.概述: LinkedHashMap 是 HashMap的子类
2.特点:
key唯一
无索引
有序
3.数据结构:
链表+哈希表
4.其他的方面和HashMap一模一样
public class Test06 {
public static void main(String[] args) {
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put("张无忌","赵敏");
map.put("张学良","于凤至");
map.put("张翠山","殷素素");
map.put("杨逍","纪晓芙");
System.out.println(map);
}
}
第二章.TreeSet
1.概述: 是Set接口的实现类
2.特点:
无索引
元素唯一
对元素进行排序
3.数据结构:基于红黑树实现的
4.构造:
TreeSet() -> 对元素进行自然排序
TreeSet(Comparator<? super E> comparator) -> 利用比较器对元素指定排序规则
public class Demo01TreeSet {
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<>();
treeSet.add("2");
treeSet.add("1");
treeSet.add("3");
treeSet.add("4");
System.out.println(treeSet);
System.out.println("==========================");
TreeSet<Person> set = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});
set.add(new Person("柳岩",36));
set.add(new Person("涛哥",18));
set.add(new Person("杨幂",32));
System.out.println(set);
}
}
public class Person {
private String name;
private int age;
public Person() {
}
public Person(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 "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
第三章.TreeMap
1.概述: TreeMap是Map接口的实现类
2.特点:
key唯一
对key进行排序
无索引
3.数据结构:红黑树
4.构造:
TreeMap() -> 对key进行自然排序
TreeMap(Comparator<? super K> comparator) ->对key按照指定规则进行排序
public class Test01 {
public static void main(String[] args) {
TreeMap<String, Integer> treeMap = new TreeMap<>();
treeMap.put("b",2);
treeMap.put("a",1);
treeMap.put("d",4);
treeMap.put("c",3);
System.out.println(treeMap);
System.out.println("=====================");
TreeMap<Person, String> treeMap1 = new TreeMap<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});
treeMap1.put(new Person("柳岩",36),"湖南");
treeMap1.put(new Person("涛哥",16),"河北");
treeMap1.put(new Person("杨幂",32),"北京");
System.out.println(treeMap1);
}
}
public class Person {
private String name;
private int age;
public Person() {
}
public Person(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 "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
第四章.HashTable和Vector集合(了解)
1.HashTable集合
1.介绍:
Map接口的实现类Hashtable, Hashtable类诞生于JDK1.0版本, Map接口诞生于JDK1.2版本. Hashtable类从JDK1.2开始,改进为实现Map接口
2.Hashtable类的特点
a.底层数据结构是哈希表
b.线程安全的,运行速度慢,被更加先进的HashMap取代
c.不允许null值,null键, 存储null直接抛出空指针异常
d.key唯一,无索引,无序
3.和HashMap的区别
HashMap:
线程不安全的,速度快,可以存储null键null值
HashTable:
线程安全的,速度慢,不可以存储null键null值,否则会出现空指针异常
2.Vector集合
1.介绍:
List接口的实现Vector,命运和Hashtable一样.
2.Vector类的特点
a.底层实现结构是数组
b.数组的默认容量是10,每次扩容是原来的长度*2
c.线程安全,运行速度慢,被ArrayList取代
第五章.Properties集合(属性集)
1.Properties 继承 HashTable
2.特点:
- 继承Hashtable,底层数据结构是哈希表。
- 线程安全,运行速度慢。
- 不允许null值,null键。
- 此集合存储键值对数据类型固定为String。
- 可以和IO流结合使用,从流中加载数据。
- 无序,无索引,key唯一
3.方法:
- Object setPropery(String key,String value),向集合中存储键值对。
- String getProperty(String key),获取集合中键对应的值,无此键返回null。
- Set<String> stringPropertyNames(),集合中的所有键存储到Set集合。
- void load(输入流对象) ,IO部分讲解。
public class Test01 {
public static void main(String[] args) {
Properties properties = new Properties();
//- Object setPropery(String key,String value),向集合中存储键值对。
properties.setProperty("username","root");
properties.setProperty("password","1234");
System.out.println(properties);
//- String getProperty(String key),获取集合中键对应的值,无此键返回null。
String username = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println(username+"..."+password);
//- Set<String> stringPropertyNames(),集合中的所有键存储到Set集合。
System.out.println("======================");
Set<String> set = properties.stringPropertyNames();
for (String key : set) {
System.out.println(properties.getProperty(key));
}
}
}
使用场景:一般都是和properties配置文件结合使用
第六章.集合嵌套
1.List嵌套List
需求:创建3个List集合,每个集合中分别存储一些字符串,将3个List集合存储到第4个List集合中。
public class Demo01ListInList {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
list1.add("孙悟空");
list1.add("猪八戒");
list1.add("沙和尚");
ArrayList<String> list2 = new ArrayList<>();
list2.add("宋江");
list2.add("林冲");
list2.add("武松");
ArrayList<String> list3 = new ArrayList<>();
list3.add("刘备");
list3.add("关羽");
list3.add("张飞");
//创建一个专门存储集合的List
ArrayList<ArrayList<String>> list = new ArrayList<>();
list.add(list1);
list.add(list2);
list.add(list3);
//遍历
//先遍历大集合,将每一个小集合遍历出来
for (ArrayList<String> arrayList : list) {
//在遍历每一个小集合,将元素从小集合中获取出来
for (String s : arrayList) {
System.out.println(s);
}
}
}
}
2.List嵌套Map
1班级有第三名同学,学号和姓名分别为:1=张三,2=李四,3=王五,2班有三名同学,学号和姓名分别为:1=黄晓明,2=杨颖,3=刘德华,4=朱丽倩,请将同学的信息以键值对的形式存储到2个Map集合中,在将2个Map集合存储到List集合中。
public class Demo02ListInMap {
public static void main(String[] args) {
HashMap<Integer, String> map1 = new HashMap<>();
map1.put(1,"张三");
map1.put(2,"李四");
HashMap<Integer, String> map2 = new HashMap<>();
map2.put(1,"黄晓明");
map2.put(2,"杨颖");
ArrayList<HashMap<Integer, String>> list = new ArrayList<>();
list.add(map1);
list.add(map2);
//遍历
//先遍历List将每个小Map遍历出来
for (HashMap<Integer, String> map : list) {
//再遍历每一个map,将map中的键值对获取出来
Set<Map.Entry<Integer, String>> entries = map.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println(entry.getKey()+"..."+entry.getValue());
}
}
}
}
3.Map嵌套Map
- JavaSE 集合 存储的是 学号 键,值学生姓名
- 1 张三
- 2 李四
- JavaEE
- 1 王五
- 2 赵柳
public class Demo03MapInMap {
public static void main(String[] args) {
HashMap<Integer, String> map1 = new HashMap<>();
map1.put(1,"张三");
map1.put(2,"李四");
HashMap<Integer, String> map2 = new HashMap<>();
map2.put(1,"王五");
map2.put(2,"赵六");
HashMap<String, HashMap<Integer, String>> hashMap = new HashMap<>();
hashMap.put("JavaSE",map1);
hashMap.put("JavaEE",map2);
//遍历
//先遍历大Map,将每一个小Map遍历出来
Set<String> set = hashMap.keySet();
for (String key : set) {
HashMap<Integer, String> map = hashMap.get(key);
//遍历小map,将小map中的key和value遍历出来
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
for (Map.Entry<Integer, String> entry : entrySet) {
Integer key1 = entry.getKey();
String value1 = entry.getValue();
System.out.println(key1+"..."+value1);
}
}
}
}
第七章.哈希表结构存储过程
1.HashMap底层数据数据结构:哈希表
2.jdk7:哈希表 = 数组+链表
jdk8:哈希表 = 数组+链表+红黑树
3.
先算哈希值,此哈希值在HashMap底层经过了特殊的计算得出
如果哈希值不一样,直接存
如果哈希值一样,再去比较内容,如果内容不一样,也存
如果哈希值一样,内容也一样,直接去重复(后面的value将前面的value覆盖)
哈希值一样->哈希冲突(哈希碰撞)
4.要知道的点:
a.在不指定长度时,哈希表中的数组默认长度为16,HashMap创建出来,一开始没有创建长度为16的数组
b.什么时候创建的长度为16的数组呢?在第一次put的时候,底层会创建长度为16的数组
c.哈希表中有一个数据加[加载因子]->默认为0.75->代表党元素存储到百分之75的时候要扩容了->2倍
d.如果对个元素出现了哈希值一样,内容不一样时,就会在同一个索引上以链表的形式存储,当链表长度>8并且当前数据长度>64时,链表就会改成使用红黑树存储
e.加入红黑树目的:查询快
外面笔试时可能会问到的变量
default_initial_capacity:HashMap默认容量 16
default_load_factor:HashMap默认加载因子 0.75
threshold:扩容的临界值 等于 容量*0.75 = 12
treeify_threshold:链表长度默认值,转为红黑树:8
min_treeify_capacity:链表被树化时最小的数组容量:64
第八章.哈希表源码分析
1.HashMap无参数构造方法的分析
//HashMap中的静态成员变量
static final float DEFAULT_LOAD_FACTOR = 0.75f;
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
解析:使用无参数构造方法创建HashMap对象,将加载因子设置为默认的加载因子,loadFactor=0.75F。
2.HashMap有参数构造方法分析
HashMap(int initialCapacity, float loadFactor) ->创建Map集合的时候指定底层数组长度以及加载因子
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);//10
}
解析:带有参数构造方法,传递哈希表的初始化容量和加载因子
- 如果initialCapacity(初始化容量)小于0,直接抛出异常。
- 如果initialCapacity大于最大容器,initialCapacity直接等于最大容器
- MAXIMUM_CAPACITY = 1 << 30 是最大容量 (1073741824)
- 如果loadFactor(加载因子)小于等于0,直接抛出异常
- tableSizeFor(initialCapacity)方法计算哈希表的初始化容量。
- 注意:哈希表是进行计算得出的容量,而初始化容量不直接等于我们传递的参数。
3.tableSizeFor方法分析
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
8 4 2 1规则->无论指定了多少容量,最终经过tableSizeFor这个方法计算之后,都会遵循8421规则去初始化列表容量
解析:该方法对我们传递的初始化容量进行位移运算,位移的结果是 8 4 2 1 码
- 例如传递2,结果还是2,传递的是4,结果还是4。
- 例如传递3,结果是4,传递5,结果是8,传递20,结果是32。
4.Node 内部类分析
哈希表是采用数组+链表的实现方法,HashMap中的内部类Node非常重要,证明HashSet是一个单向链表
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
解析:内部类Node中具有4个成员变量
- hash,对象的哈希值
- key,作为键的对象
- value,作为值得对象(讲解Set集合,不牵扯值得问题)
- next,下一个节点对象
5.存储元素的put方法源码
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
解析:put方法中调研putVal方法,putVal方法中调用hash方法。
- hash(key)方法:传递要存储的元素,获取对象的哈希值
- putVal方法,传递对象哈希值和要存储的对象key
6.putVal方法源码
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
解析:方法中进行Node对象数组的判断,如果数组是null或者长度等于0,那么就会调研resize()方法进行数组的扩容。
7.resize方法的扩容计算
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
解析:计算结果,新的数组容量=原始数组容量<<1,也就是乘以2。
8.确定元素存储的索引
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
解析:i = (数组长度 - 1) & 对象的哈希值,会得到一个索引,然后在此索引下tab[i],创建链表对象。
不同哈希值的对象,也是有可能存储在同一个数组索引下。
其中resize()扩容的方法,默认是16
tab[i] = newNode(hash, key, value, null);->将元素放在数组中 i就是索引
i = (n - 1) & hash
0000 0000 0000 0000 0000 0000 0000 1111->15
& 0&0=0 0&1=0 1&1=1
0000 0000 0000 0001 0111 1000 0110 0011->96355
--------------------------------------------------------
0000 0000 0000 0000 0000 0000 0000 0011->3
0000 0000 0000 0000 0000 0000 0000 1111->15
& 0&0=0 0&1=0 1&1=1
0000 0000 0001 0001 1111 1111 0001 0010->1179410
--------------------------------------------------------
0000 0000 0000 0000 0000 0000 0000 0010->2
9.遇到重复哈希值的对象
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
解析:如果对象的哈希值相同,对象的equals方法返回true,判断为一个对象,进行覆盖操作。
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;
}
解析:如果对象哈希值相同,但是对象的equals方法返回false,将对此链表进行遍历,当链表没有下一个节点的时候,创建下一个节点存储对象。