集合map,HashMap源码学习
This commit is contained in:
parent
7d9b21790c
commit
cbf03608b0
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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及其实现类:HashMap、HashTable、LinkedHashMap、TreeMap、Properties
|
||||||
|
* 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的内部实现是一个HashMap,TreeSet的内部实现是一个TreeMap,LinkedHashSet的内部实现是一个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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue