集合map,HashMap源码学习

This commit is contained in:
dingjiawen 2022-09-19 18:27:18 +08:00
parent 7d9b21790c
commit cbf03608b0
2 changed files with 265 additions and 0 deletions

View File

@ -0,0 +1,99 @@
package com.markilue.java_learning.collection;
import org.junit.Test;
import java.util.HashMap;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.collection
* @Author: dingjiawen
* @CreateTime: 2022-09-19 17:03
* @Description: TODO 测试HashMap的树化和反树化
* @Version: 1.0
*/
public class HashMapTest {
@Test
public void test1(){
//这里为了演示的效果我们造一个特殊的类这个类的hashCode方法返回固定值1
//因为这样就可以造成冲突问题使得它们都存到table[1]
HashMap<MyKey, String> map = new HashMap<>();
for (int i = 1; i <= 11; i++) {
map.put(new MyKey(i), "value"+i);//树化演示
}
}
@Test
public void test2(){
HashMap<MyKey, String> map = new HashMap<>();
for (int i = 1; i <= 11; i++) {
map.put(new MyKey(i), "value"+i);
}
for (int i = 1; i <=11; i++) {
map.remove(new MyKey(i));//反树化演示
}
}
@Test
public void test3(){
HashMap<MyKey, String> map = new HashMap<>();
for (int i = 1; i <= 11; i++) {
map.put(new MyKey(i), "value"+i);
}
for (int i = 1; i <=5; i++) {
map.remove(new MyKey(i));
}//table[1]下剩余6个结点
for (int i = 21; i <= 100; i++) {
map.put(new MyKey(i), "value"+i);//添加到扩容时反树化
}
}
}
class MyKey{
int num;
public MyKey(int num) {
super();
this.num = num;
}
@Override
public int hashCode() {
//容量小于20全放一个里面
if(num<=20){
return 1;
}else{
//大于30之后尽可能分开
final int prime = 31;
int result = 1;
result = prime * result + num;
return result;
}
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyKey other = (MyKey) obj;
if (num != other.num)
return false;
return true;
}
}

View File

@ -0,0 +1,166 @@
package com.markilue.java_learning.collection;
import org.junit.Test;
import java.util.*;
/**
* @BelongsProject: java_learning
* @BelongsPackage: com.markilue.java_learning.collection
* @Author: dingjiawen
* @CreateTime: 2022-09-19 15:35
* @Description:
* TODO Map及其实现类:HashMapHashTableLinkedHashMapTreeMapProperties
* 1)Map:
* 1.这些实现类均继承自java.util.Map<key,value>接口
* 2.使用put方法时若指定的键(key)在集合中没有则没有这个键对应的值返回null并把指定的键值添加到集合中
* 若指定的键(key)在集合中存在则返回值为集合中键对应的值该值为替换前的值并把指定键所对应的值替换成指定的新值
* 3.Map的遍历不能支持foreach因为Map接口没有继承java.lang.Iterable<T>接口也没有实现Iterator iterator()方法
* 但是可以分别遍历其key(keySet()方法)和value(values方法),也可以成对遍历<key,value>(entrySet()方法)
* 4.Map.Entry是Map接口的内部接口每一种Map内部有自己的Map.Entry的实现类
* 在Map中存储数据实际上是将Key---->value的数据存储在Map.Entry接口的实例中再在Map集合中插入Map.Entry的实例化对象
* 2)HashMap:
* 1.底层是哈希表 数组加链表 JDK1.7散列表table[index]也称为bucket桶,其中table是一个HashMap.Entry<Key,value>是一个链表,HashMap.Entry中还存放着next指针存放相同hashcode值的元素
* JDK1.8封装为HashMap.Node类型或HashMap.TreeNode类型它俩都直接或间接的实现了Map.Entry接口即table[index]下的映射关系可能串起来一个链表或一棵红黑树自平衡的二叉树
* 链表长度大于8(且总容量大于等于64,不大的话会优先扩容而不是树化)转为红黑树一种解释是节点规律服从泊松分布经过计算得出这个值会比较好,树节点数小于6退化为链表
* 2.评价两个key是否相等的指标是其hashcode相等,且equal方法返回true,所以存放的对象必须实现hashcode和equal方法
* 3.线程不安全的并允许使用 null 值和 null
* 4.默认初始化长度1<<4 即16(一定是2的倍数).每次扩容变为原来的两倍未避免分布不均匀增加了扰动计算
* 5.哈希算法:使用位运算代替取模运算(前提:容量length一定是2^n) 源码hashcode&(length-1) X %2^n=X &(2^n-1)
* 3)HashTable:
* 1.底层实现是哈希表
* 2.评价两个key是否相等的指标是其hashcode相等,且equal方法返回true,所以存放的对象必须实现hashcode和equal方法
* 3.线程安全的任何非 null 对象都可以用作键或值
* 4.默认初始化长度11(素数)内次扩容变为原来的2n+1倍,扩容结果也为奇数素数使得节点取模更加均匀分散效果越好
* 5.哈希算法:在保证是正数的情况下直接取模 源码:(hashcode & 0x7FFFFFFF)%table.length
* 4)LinkedHashMap:
* 1.LinkedHashMap是HashMap的子类
* 2.与HashMap的不同在于:前者维护着一个运行于所有条目的双重链接列表此链接列表定义了迭代顺序该迭代顺序通常就是将键插入到映射中的顺序插入顺序
* 5)TreeMap:
* 1.基于红黑树Red-Black tree NavigableMap 实现
* 2.该映射根据其键的自然顺序进行排序或者根据创建映射时提供的 Comparator 进行排序具体取决于使用的构造方法
* 6)Properties:
* 1.Properties 类是 Hashtable 的子类Properties 可保存在流中或从流中加载属性列表中每个键及其对应值都是一个字符串
* 2.存取数据时建议使用setProperty(String key,String value)方法和getProperty(String key)方法
* 7)HashSet与Map的关系
* 1.Set的内部实现其实是一个Map即HashSet的内部实现是一个HashMapTreeSet的内部实现是一个TreeMapLinkedHashSet的内部实现是一个LinkedHashMap
* 2.Set中只有一个元素又是怎么变成(key,value)的呢
* 源码实现private static final Object PRESENT = new Object();
* public boolean add(E e) {
* return map.put(e, PRESENT)==null;
* }
* public Iterator<E> iterator() {
* return map.keySet().iterator();
* }
* @Version: 1.0
*/
public class MapTest {
//HashMap的put覆盖测试和可以存放null值测试
@Test
public void test(){
HashMap<String,Double> map = new HashMap<>();
map.put("张三", 10000.0);
//key相同新的value会覆盖原来的value
//因为String重写了hashCode和equals方法
map.put("张三", 12000.0);
map.put("李四", 14000.0);
//HashMap支持key和value为null值
String name = null;
Double salary = null;
map.put(name, salary);
Set<Map.Entry<String, Double>> entrySet = map.entrySet();
for (Map.Entry<String, Double> entry : entrySet) {
System.out.println(entry);//null=null;李四=14000.0;张三=12000.0
}
}
//LinkedHashMap的插入顺序测试
@Test
public void test1(){
LinkedHashMap<String,Double> map = new LinkedHashMap<>();
map.put("张三", 10000.0);
//key相同新的value会覆盖原来的value
//因为String重写了hashCode和equals方法
map.put("张三", 12000.0);
map.put("李四", 14000.0);
//HashMap支持key和value为null值
String name = null;
Double salary = null;
map.put(name, salary);
Set<Map.Entry<String, Double>> entrySet = map.entrySet();
for (Map.Entry<String, Double> entry : entrySet) {
System.out.println(entry); //张三=12000.0;李四=14000.0;null=null
}
}
//TreeMap自然顺序比较
@Test
public void test2() {
TreeMap<String,Integer> map = new TreeMap<>();
map.put("Jack", 11000);
map.put("Alice", 12000);
map.put("zhangsan", 13000);
map.put("baitao", 14000);
map.put("Lucy", 15000);
//String实现了Comparable接口默认按照Unicode编码值排序
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
for (Map.Entry<String, Integer> entry : entrySet) {
System.out.println(entry);
/*
Alice=12000
Jack=11000
Lucy=15000
baitao=14000
zhangsan=13000
*/
}
}
//TreeMap实现comparator比较器顺序
@Test
public void test3() {
//指定定制比较器Comparator按照Unicode编码值排序但是忽略大小写
TreeMap<String,Integer> map = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareToIgnoreCase(o2);
}
});
map.put("Jack", 11000);
map.put("Alice", 12000);
map.put("zhangsan", 13000);
map.put("baitao", 14000);
map.put("Lucy", 15000);
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
for (Map.Entry<String, Integer> entry : entrySet) {
System.out.println(entry);
/*
Alice=12000
baitao=14000
Jack=11000
Lucy=15000
zhangsan=13000
*/
}
}
//properties方法测试
@Test
public void test4(){
// Properties properties1 = new Properties();
// properties1.setProperty()
Properties properties = System.getProperties();
String p2 = properties.getProperty("file.encoding");//当前源文件字符编码
System.out.println(p2);
}
}