热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

HashMap的一些理解

#Map##什么是MapMap翻译为映射1.Map是接口,在java.util包2.Java中提供的面向“查询”的API-查询中输入的被

# Map


## 什么是Map

Map 翻译为 映射


1. Map是接口,在java.util包

2. Java 中提供的面向“查询”的API
- 查询中输入的被检索信息称为 “Key”,key翻译为关键信息
- 查询结果称为“Value”, Value 翻译为“值”

- Map提供了根据Key查询Value的功能

3. Map接口有两个常用实现类
- 最重要的实现类 HashMap,Hash 散列,HashMap也称为散列表,哈希表
- HashMap是查询**最快**的 API

- TreeMap 采用二叉树算法。 相当于2分查找,速度很快。

4. Map 中key不允许重复,Value可以重复

5. 尽量将软件中的查找功能用Map进行优化!提高软件的性能。


## 使用Map


1. 创建map对象,是空的,没有数据。

Map map = new HashMap();

2. 将被查询的 Key-Value 成对的存储到map


Object obj = map.put(key, value);

> 如果第一次put,返回null,如果第二次put同样的key,返回被替换的对象


3. 查询时候,根据Key查询到Value


value = map.get(key);


> 如果没有找到返回null


案例: 使用put方法:


public class MapPutDemo {

public static void main(String[] args) {
/*
* 将数据添加到Map中
*/
Map map = new HashMap();
//第一次将“u1”-"Tom"添加到到map
Object obj = map.put("u1", "Tom");
//第一次返回null
System.out.println(obj);
//第二次将“u1”的value替换为Jerry
obj = map.put("u1", "Jerry");
//返回被替换的Tom
System.out.println(obj);
//显示Map中的内容 ?思考如下输出用了那个方法
System.out.println(map); //toString()

/*
* 利用泛型定义类型安全的map集合 
*/
Map map2=
new HashMap<>();
//Java 6 中不能省略
//Map map3=
// new HashMap();
String s = map2.put("u1", "Tom");
System.out.println(s);
s = map2.put("u1", "Jerry");
System.out.println(s);
System.out.println(map2); 

}
}


案例: 使用get方法


public class MapGetDemo {

public static void main(String[] args) {
/*
* 测试map的查询方法get
* 1. 如果找到就返回key对应的值
* 2. 如果没有找到返回 null
*/
Map map=
new HashMap<>();
//向map中添加数据
map.put("u1", "Tom");
map.put("u2", "Jerry");
//从map中检索
String v1 = map.get("u1");
String v2 = map.get("u3");
System.out.println(v1);
System.out.println(v2);//null
}

}

## HashMap查询性能优异


案例: 比较HashMap查询性能:


public class MapTestDemo {
public static void main(String[] args) {
/*
* 纵向比较 map 的查询性能
*/
test(1000);
test(10000);
test(100000);
/*
* 横向对比 HashMap和LinkedList的查询性能 
*/
testLinkedList(10000);
testLinkedList(100000);
}
/**
* 测试LinkedList的查找性能
* LinkedList 采用双向循环链表结构,
* 它的头尾查询性能好,中部查找性能差
*/
public static void testLinkedList(int n){
LinkedList list=
new LinkedList<>();
for(int i=0; i list.add("u"+i);
}
//从LinkedList中部查询数据
long t1=System.nanoTime();
String str = list.get(n/2);
long t2=System.nanoTime();
System.out.println(str);
System.out.println(t2-t1);
}

/**
* 性能测试方法
*/
public static void test(int n){
Map map=
new HashMap<>();
for(int i=0; i //i=0 1 2 ... n-1
String val = "n"+i;
map.put(i, val);
}
//nanoTime() 返回系统纳秒数1ms=1000000ns
long t1 = System.nanoTime();
String v = map.get(n-1);
long t2 = System.nanoTime();
System.out.println(v); 
System.out.println(t2-t1);
}
}


## Map 的API方法


0. get() 在map中根据key查询得到指定的value
1. size() 检测map集合中元素的个数
2. isEmpty() 检测map集合是否为空
3. remove() 删除map集合中的指定元素,返回被删除的value
4. clear() 清空map集合
5. containsKey() 检测map集合中是否包含指定的key


案例:

public class MapRemoveDemo {

public static void main(String[] args) {
/*
* 演示Map的方法
* size() 集合中元素的数量
* isEmpty() 检测集合是否为空
* remove()  删除集合中的元素
*/
Map map=
new HashMap<>();
System.out.println(map.size());
System.out.println(map.isEmpty()); 
map.put("u1", "Tom");
map.put("u2", "Jerry");
map.put("u3", "Andy");
map.put("u4", "John");
System.out.println(map.size());
System.out.println(map.isEmpty());
//从map中删除u1元素,返回被删除的value
String val = map.remove("u1");
System.out.println(val);
System.out.println(map.size());
System.out.println(map.isEmpty()); 
//清空map集合的内容
map.clear();
System.out.println(map.size());//0
System.out.println(map.isEmpty());//true
}
}




## HashMap 的工作原理


1. HashMap内部利用数组存储数据。
2. 根据key的hashCode值计算出数组的下标位置,进行添加或者查询数据。
- 根据hashCode计算出数组的下标位置的算法称为“散列算法”
3. 数组下标位置会重复,重复时候利用链表存储重复元素
- 这个链表称为 “散列桶” 
4. 添加和查询时候如果有散列桶,则根据equals方法逐个比较找到位置。


> 由于利用hashCode直接定位到数组的存储位置,无需依次查找,所以HashMap具有极高查找性能。


![](map.png)


影响HashMap的查找性能因素:


1. 如果数据多,而数组容量少,大量数据重复的存储在散列桶中,造成在散列桶中进程大量的顺序查找,性能差。 
- 解决办法是提供更多数组容量,减少散列桶中重复的数据。
- 如果保持 元素的总数和数组容量的比值少于 75% 时候,出现重复位置的情况少于3个!
- HashMap中默认的“加载因子” 就是75%, HashMap中添加元素时候,HashMap始终会保持元素和数组容量的比值小于75%,如果超过75%则进行数组扩容“重新散列”


2. hashCode 方法
- Java 在 Object类上定义了hashCode方法,用于支持HashMap中的算法。
- 作为key的类型必须很好的实现 hashCode方法,否则会影响HashMap性能
- 当两个对象 equals方法比较结果为true时候,他们的hashCode相同
- 当两个对象 equals方法比较结果为false时候,他们的hashCode近可能不同!


3. equals 方法
- HashMap添加或查找时候,先根据hashCode计算数组下标位置,然后在利用equals比较key对象是否相同。
- 如果key的hashCode和equals方法不"一致", 会造成HashMap工作异常!可能重复添加或者查找不到数据。

> 建议:一定成对重写key的equals方法和hashCode方法。


> java 中的 API String,Integer 等都成对的重写了equals和hashCode。


4. 创建HashMap的性能优化参数
- new HashMap(数组容量,加载因子)
- 默认 new HashMap() 等价于 new HashMap(16, 0.75f)
- 在添加到12个元素以后进行扩容。
- 如果事先可以预测添加到HashMap中数量,则可以声明足够的大的容量,避免反复扩容浪费时间。
- 如果有 1000 条数据需要添加到HashMap,则new HashMap(1500)  


一个有故障的案例:


public class MapHashCodeDemo {
public static void main(String[] args) {
/*
* 作为key的类,如果不很好的重写equals 和
* hashCode方法会造成 HashMap工作故障
*/
HashMap map=
new HashMap<>();
map.put(new Player(1), "吃鸡");
map.put(new Player(2), "英雄联盟");
map.put(new Player(3), "英雄联盟");
Player One= new Player(1);
String game=map.get(one);
System.out.println(game); 
}
}
class Player{
int id;
public Player(int id) {
this.id = id;
}

}


添加 hashCode和equals解决故障:


public class MapHashCodeDemo {
public static void main(String[] args) {
/*
* 作为key的类,如果不很好的重写equals 和
* hashCode方法会造成 HashMap工作故障
*/
HashMap map=
new HashMap<>();
map.put(new Player(1), "吃鸡");
map.put(new Player(2), "英雄联盟");
map.put(new Player(3), "英雄联盟");
Player One= new Player(1);
String game=map.get(one);
System.out.println(game); 
}
}
class Player{
int id;
public Player(int id) {
this.id = id;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Player other = (Player) obj;
if (id != other.id)
return false;
return true;
}

}



## Map 的遍历(迭代)



public class MapEnrtySetDemo {

public static void main(String[] args) {
/*
* 利用EntrySet对map集合进行遍历
*/
Map map=
new HashMap<>();
//如下数据相当于从浏览器收到的请求头
map.put("Host", "doc.tedu.cn");
map.put("Connection", "keep-alive");
map.put("Cache-Control", "max-age=0");
//...
//遍历全部的请求头
//map 没有提供之间遍历的方法!
//可以利用 entrySet 和 keySet 间接实现遍历
// Entry 对象就代表map中的key-value对
// 一个Entry 对象中包含两个属性,一个是key
// 一个是Value,Entry的实现类是HashMap内部类
Set> set = 
map.entrySet();
//set 中包含map中全部的key-value对
//只要遍历 set 就相当于遍历了map
for(Entry e: set){
System.out.println(
e.getKey()+", "+e.getValue());
}
}

}
































推荐阅读
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
author-avatar
Chickny的造梦空间
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有