怎么向调用的父类传hashMap子类对象调用父类函数

相关文章推荐
职责链模式的优缺点和使用环境:
1.优点:(1)职责连模式使得一个对象无需知道是其他哪一个对象处理其请求,仅需知道该请求会被处理即可,接受者和发送者都没有对方的明确信息,且链中的对象不需要知道链...
击鼓传花是一种热闹而又紧张的饮酒游戏。
在酒宴上宾客依次坐定位置,由一人击鼓,击鼓的地方与传花的地方是分开的,以示公正。
开始击鼓时,花束就开始依次传递,鼓声一落,假如花束在某人手中,则该...
Map中的每个成员方法由一个关键字(key)和一个值(value)构成。Map接口不直接继承于Collection接口,因为它包装的是一组成对的“键-值”对象的集合,而且在M...
在本文中如果您发现了错误,请您花费几分钟的时间给予指出,谢谢!!
本文主要总结Map接口及其重要实现类的用法。
三.Map接口
Map中的每个成员方法由一个关键字(key)和...
Map中的每个成员方法由一个关键字(key)和一个值(value)构成。Map接口不直接继承于Collection接口,因为它包装的是一组成对的“键-值”对象的集合,而且在M...
Map中的每个成员方法由一个关键字(key)和一个值(value)构成。Map接口不直接继承于Collection接口,因为它包装的是一组成对的“键-值”对象的集合,而且在Map接口的集合...
Map接口,是和Collection接口并列的一个接口,它其实是一个有着映射关系的集合"key-value(也可以叫entry)"(就像数学中的函数一样),由于key存储时用的是set来存储,所以ke...
上节聊到「Map接口和实现类」,今天我们深入探讨其实现类中的HashMap如何进行底层实现。
Hashmap基本结构讲解
哈希表的基本结构就是“数组+链表”。我们打开HashMap...
【JavaSE_学习笔记】Map接口及其子实现类Map接口如果需要存储成对存在的数据,就要使用双列集合
实现了Map接口的集合类具备的特点:存储的数据以键值对的形式存在,键(key)不能重复,值(v...
Map代表具有映射关系的集合,
该接口中的方法如下:
需要强调的是:
size():Map集合中的映射条数如果大于Integer.MAX_VALUE,将返回Integer....
他的最新文章
讲师:Array
讲师:李志伟
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)Java 的子类构造器与其父类关系
简单谈一下 Java 程序设计语言中,子类构造器与其父类构造器的关系。 Java 是属于单继承的对象语言,子类可以继承并且使用其父类的所有变量和方法,非 常典型的例子是 Java 中所有类都是继承于超级类 Object, 因此每一个类都可以使用 Object 类中的变量和方法。子类在继承父类时需要 extends 标记继承的父类,例如 public class sun extends father{},其中子类 sun 继承了父类 father。然而,有时候你会发现,在子 类的构造器中没用使用 super()调用其父类相应的构造器时,会出现错误无法编译,而有 的时候又不需要在子类构造器中使用 super()调用父类相应的构造器,为什么嘞?可以来 研究一下。 什么时候需要使用 super()调用父类的构造器,仔细分析起来可以有多种情况,但是 我们只要看到调用的本质就能很容易判断什么时候需要调用父类的构造器以及怎么调用。 上面提到,Java 中的子类可以使用父类的所有变量和方法,因此子类都可以往上强制转换 为父类的对象类型, 但是转换后的对象不能使用子类中相对于父类特有的东西。 我们可以从 这个现象中发现一点东西, 我们可以这么理解为子类就是在实现父类的基础上, 再加上子类 本身特有的一点变量和方法,也就是说子类=父类+新增的变量+新增的方法。对象语言的构 造器作用在于对类创建实例对象时完成对象的初始化工作 (为变量申请内存空间等) 因此, , 子类的构造器除了承担着初始化子类的新增变量和方法, 还要初始化父类自身新增加的变量 和方法。一般来说,如果在子类的构造器中没有使用 super()调用父类相应的构造器,子 类的构造器会自动调用父类默认的, 也就是不带参数的构造器来初始化父类的东西。 这里又 要谈到 Java 类的默认构造器,当你没有为你写的类建造构造器的时候,Java 会自动帮你创 建这个默认构造器, 但是当你自己编写了构造器后, 你自己编写构造器将会覆盖掉默认的构 造器, 就是说默认的构造器不存在。 那么这个时候, 你在继承的子类里面没有显示添加 super ()函数调用你在父类里编写的父类构造器,子类的构造器将不能自动找到父类的构造器。 找不到父类的构造器意味着什么?意味着你的子类实例化的时候找不到父类的构造器来初 始化父类的东西,不能完成类实例对象的初始化工作,你说你的子类能不出现错误?! 所以,在子类的构造器中需不需要添加 super()调用父类相应的构造器,只需要考虑 子类的构造器才初始化的时候能不能完成所有变量和方法的初始化就可以了。 另外, 根据自 己初始化工作的需要使用 super()调用相应的父类构造器来初始化也是有必要的。
Java类、对象、构造器...
java构造器与方法...
Java的子类调用父类的构造方法...
JAVA的构造器问题...
Java入门:理解构造器-构造器和方法区别...
java定义类、属性、方法和构造器...
JAVA 对象、构造和使用类...
java 类与构造方法...
Java_05_类的构建与对象的使用...
Java入门理解构造器...
■ 网友在搜博客分类:
在很多时候,我们需要在类的内部初始化一个静态的Map或者List,然后保存一下常量值提供给类内部方法使用。
我们通常的做法是:
首先初始化一个Map的静态变量。
然后在静态块添加常量值:
private final static Map&String, String& CONSTANT =
new HashMap&String, String&();
CONSTANT.put("1", "one");
CONSTANT.put("2", "two");
其实还可以这么写:
private final static Map&String, String& CONSTANT =
new HashMap&String, String&() {
put("1", "one");
put("2", "two");
如果对于这种方式比较陌生,那先看一个熟悉的:
new Thread() {
public void run() {
System.out.println("Thread running!");
}.start();
实际上上面这段代码的意思就是,声明一个Thread的子类并重写Thread的run()方法,然后创建一个该子类的实例然后调用其start()方法。由于声明的该Thread的子类没有名字,所以叫匿名类。又由于没有名字的类只能存在于一个类或者一个方法内部,所以又称为匿名内部类。
匿名内部类的语法也可以这么写:
Thread thread = new Thread() {
public void run() {
System.out.println("Thread running!");
thread.start();
唯一的区别就是不是直接创建子类并调用其方法,而是声明一个该子类的父类引用thread,然后通过该父类引用调用子类方法。
创建完匿名类的实例后,没有立即执行start(),创建实例和执行实例的方法分开。
两者的区别相当于:
new User().setName("Boyce Zhang");
User user = new User();
user.setName("Boyce Zhang");
匿名内部类的另一个语法场景:
new Thread() {
public void run() {
System.out.println("Thread running!");
实际上这种写法就是在匿名子类的类局部代码块中调用其类方法。
局部代码块内的语句是在创建该类的实例后由类加载器隐式立即执行的。
相当于:
public class MyThread extends Thread {
public void run() {
System.out.println("Thread running!");
所以三种方式在执行的时刻上略微的差别之外,效果并没有太大的区别。
这样一来,前面初始化Map的方式就不难理解了:
private final static Map&String, String& CONSTANT = new HashMap&String, String&() {
put("1", "one");
put("2", "two");
原理就是:
声明并实例化一个HashMap的子类(子类没有重写父类HashMap的任何方法),并且在子类的类局部代码块调用父类HashMap的put()方法。
最后声明一个Map接口引用CONSTANT指向实例化的HashMap子类的实例。
根据前面的例子我们知道,类局部代码块中的put()方法调用将在HashMap的匿名子类被实例化后由类加载器隐式的执行。
其实,对于Java的任何类或接口,都可以声明一个匿名类继承或实现它。如:
//重写父类方法,局部代码块调用自己重写过的父类方法。
List&String& list = new ArrayList&String&() {
public boolean add(String e) {
System.out.println("Cannot add anything!");
//代码块的顺序在前后都无所谓,可以出现在类范围的任何位置。
add("Boyce Zhang");
//局部代码块调用父类方法。
dao.add(new User(){
setName("Boyce Zhang");
setAge(26);
//重写父类方法
ThreadLocal&User& threadLocal = new ThreadLocal&User&() {
protected String initialValue() {
return new User("Boyce Zhang", 26);
在匿名类的内部我们不但可以实现或重写其父类的方法。
而且也可以在其类的局部代码块中执行自己的方法或者其父类的方法。
这并不是匿名内部类的特殊语法,而是Java的语法,对于任何类都适用。
这种写法常常就是用在实例化一个类后立即执行某些方法做一些类实例的数据初始化什么的。
其作用和先实例化一个类,在使用其引用调用需要立即调用的方法是一样的,如:
Map&String, String& map = new HashMap&String, String&();
map.put("1", "one");
map.put("2", "two");
这种语法的优点就是简单,实例化一个类后立即做一些事情,比较方便。
效果有一点儿像Javascript里的即时函数一样。但是有本质的区别。
因为Javascript没有类的概念,或者说Javascript中function就是类,类就是function,所以即时函数是加载完后执行整个function。而Java的局部代码块是可以选择执行类的任何方法。
当然这种写法也有其缺点:
每一个内部类的实例都会隐性的持有一个指向外部类的引用(静态内部类除外),这样一方面是多余的引用浪费,另一方面当串行化这个子类实例时外部类也会被不知不觉的串行化,如果外部类没有实现serialize接口时,就会报错。
浏览 17700
另一方面当串行化这个子类实例时外部类也会被不知不觉的串行化,如果外部类没有实现serialize接口时,就会报错。这个是怎么回事?博主有例子吗
该用户名已经存在
浏览: 207103 次
来自: 上海
引用另一方面当串行化这个子类实例时外部类也会被不知不觉的串行化 ...
不过受教了 ,不过稍稍修改 还是很好用的 。顶
程序还是有BUG
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'JAVA HashMap详细介绍和示例
字体:[ ] 类型:转载 时间:
我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap。
第1部分 HashMap介绍HashMap简介HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。HashMap 的实例有两个参数影响其性能:“初始容量” 和 “加载因子”。容量 是哈希表中桶的数量,初始容量 只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。通常,默认加载因子是 0.75, 这是在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。
HashMap的继承关系HashMap与Map关系如下图:HashMap的构造函数HashMap共有4个构造函数,如下: 代码如下:// 默认构造函数。HashMap()// 指定“容量大小”的构造函数HashMap(int capacity)// 指定“容量大小”和“加载因子”的构造函数HashMap(int capacity, float loadFactor)// 包含“子Map”的构造函数HashMap(Map&? extends K, ? extends V& map)HashMap的API 代码如下:void&&&&&&&&&&&&&&&& clear()Object&&&&&&&&&&&&&& clone()boolean&&&&&&&&&&&&& containsKey(Object key)boolean&&&&&&&&&&&&& containsValue(Object value)Set&Entry&K, V&&&&&& entrySet()V&&&&&&&&&&&&&&&&&&& get(Object key)boolean&&&&&&&&&&&&& isEmpty()Set&K&&&&&&&&&&&&&&& keySet()V&&&&&&&&&&&&&&&&&&& put(K key, V value)void&&&&&&&&&&&&&&&& putAll(Map&? extends K, ? extends V& map)V&&&&&&&&&&&&&&&&&&& remove(Object key)int&&&&&&&&&&&&&&&&& size()Collection&V&&&&&&&& values()第2部分 HashMap源码解析为了更了解HashMap的原理,下面对HashMap源码代码作出分析。在阅读源码时,建议参考后面的说明来建立对HashMap的整体认识,这样更容易理解HashMap。 代码如下:package java.import java.io.*;public class HashMap&K,V&&&& extends AbstractMap&K,V&&&& implements Map&K,V&, Cloneable, Serializable{&&& // 默认的初始容量是16,必须是2的幂。&&& static final int DEFAULT_INITIAL_CAPACITY = 16;&&& // 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)&&& static final int MAXIMUM_CAPACITY = 1 && 30;&&& // 默认加载因子&&& static final float DEFAULT_LOAD_FACTOR = 0.75f;&&& // 存储数据的Entry数组,长度是2的幂。&&& // HashMap是采用拉链法实现的,每一个Entry本质上是一个单向链表&&& transient Entry[]&&& // HashMap的大小,它是HashMap保存的键值对的数量&&&&&& // HashMap的阈值,用于判断是否需要调整HashMap的容量(threshold = 容量*加载因子)&&&&&& // 加载因子实际大小&&& final float loadF&&& // HashMap被改变的次数&&& transient volatile int modC&&& // 指定“容量大小”和“加载因子”的构造函数&&& public HashMap(int initialCapacity, float loadFactor) {&&&&&&& if (initialCapacity & 0)&&&&&&&&&&& throw new IllegalArgumentException("Illegal initial capacity: " +&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& initialCapacity);&&&&&&& // HashMap的最大容量只能是MAXIMUM_CAPACITY&&&&&&& if (initialCapacity & MAXIMUM_CAPACITY)&&&&&&&&&&& initialCapacity = MAXIMUM_CAPACITY;&&&&&&& if (loadFactor &= 0 || Float.isNaN(loadFactor))&&&&&&&&&&& throw new IllegalArgumentException("Illegal load factor: " +&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& loadFactor);&&&&&&& // 找出“大于initialCapacity”的最小的2的幂&&&&&&& int capacity = 1;&&&&&&& while (capacity & initialCapacity)&&&&&&&&&&& capacity &&= 1;&&&&&&& // 设置“加载因子”&&&&&&& this.loadFactor = loadF&&&&&&& // 设置“HashMap阈值”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。&&&&&&& threshold = (int)(capacity * loadFactor);&&&&&&& // 创建Entry数组,用来保存数据&&&&&&& table = new Entry[capacity];&&&&&&& init();&&& }
&&& // 指定“容量大小”的构造函数&&& public HashMap(int initialCapacity) {&&&&&&& this(initialCapacity, DEFAULT_LOAD_FACTOR);&&& }&&& // 默认构造函数。&&& public HashMap() {&&&&&&& // 设置“加载因子”&&&&&&& this.loadFactor = DEFAULT_LOAD_FACTOR;&&&&&&& // 设置“HashMap阈值”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。&&&&&&& threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);&&&&&&& // 创建Entry数组,用来保存数据&&&&&&& table = new Entry[DEFAULT_INITIAL_CAPACITY];&&&&&&& init();&&& }&&& // 包含“子Map”的构造函数&&& public HashMap(Map&? extends K, ? extends V& m) {&&&&&&& this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,&&&&&&&&&&&&&&&&&&&&& DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);&&&&&&& // 将m中的全部元素逐个添加到HashMap中&&&&&&& putAllForCreate(m);&&& }&&& static int hash(int h) {&&&&&&& h ^= (h &&& 20) ^ (h &&& 12);&&&&&&& return h ^ (h &&& 7) ^ (h &&& 4);&&& }&&& // 返回索引值&&& // h & (length-1)保证返回值的小于length&&& static int indexFor(int h, int length) {&&&&&&& return h & (length-1);&&& }&&& public int size() {&&&&&&&&&& }&&& public boolean isEmpty() {&&&&&&& return size == 0;&&& }&&& // 获取key对应的value&&& public V get(Object key) {&&&&&&& if (key == null)&&&&&&&&&&& return getForNullKey();&&&&&&& // 获取key的hash值&&&&&&& int hash = hash(key.hashCode());&&&&&&& // 在“该hash值对应的链表”上查找“键值等于key”的元素&&&&&&& for (Entry&K,V& e = table[indexFor(hash, table.length)];&&&&&&&&&&&& e !=&&&&&&&&&&&& e = e.next) {&&&&&&&&&&& O&&&&&&&&&&& if (e.hash == hash && ((k = e.key) == key || key.equals(k)))&&&&&&&&&&&&&&& return e.&&&&&&& }&&&&&&&&&& }&&& // 获取“key为null”的元素的值&&& // HashMap将“key为null”的元素存储在table[0]位置!&&& private V getForNullKey() {&&&&&&& for (Entry&K,V& e = table[0]; e != e = e.next) {&&&&&&&&&&& if (e.key == null)&&&&&&&&&&&&&&& return e.&&&&&&& }&&&&&&&&&& }&&& // HashMap是否包含key&&& public boolean containsKey(Object key) {&&&&&&& return getEntry(key) !=&&& }&&& // 返回“键为key”的键值对&&& final Entry&K,V& getEntry(Object key) {&&&&&&& // 获取哈希值&&&&&&& // HashMap将“key为null”的元素存储在table[0]位置,“key不为null”的则调用hash()计算哈希值&&&&&&& int hash = (key == null) ? 0 : hash(key.hashCode());&&&&&&& // 在“该hash值对应的链表”上查找“键值等于key”的元素&&&&&&& for (Entry&K,V& e = table[indexFor(hash, table.length)];&&&&&&&&&&&& e !=&&&&&&&&&&&& e = e.next) {&&&&&&&&&&& O&&&&&&&&&&& if (e.hash == hash &&&&&&&&&&&&&&&&& ((k = e.key) == key || (key != null && key.equals(k))))&&&&&&&&&&&&&&&&&&&&&& }&&&&&&&&&& }&&& // 将“key-value”添加到HashMap中&&& public V put(K key, V value) {&&&&&&& // 若“key为null”,则将该键值对添加到table[0]中。&&&&&&& if (key == null)&&&&&&&&&&& return putForNullKey(value);&&&&&&& // 若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。&&&&&&& int hash = hash(key.hashCode());&&&&&&& int i = indexFor(hash, table.length);&&&&&&& for (Entry&K,V& e = table[i]; e != e = e.next) {&&&&&&&&&&& O&&&&&&&&&&& // 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!&&&&&&&&&&& if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {&&&&&&&&&&&&&&& V oldValue = e.&&&&&&&&&&&&&&& e.value =&&&&&&&&&&&&&&& e.recordAccess(this);&&&&&&&&&&&&&&& return oldV&&&&&&&&&&& }&&&&&&& }&&&&&&& // 若“该key”对应的键值对不存在,则将“key-value”添加到table中&&&&&&& modCount++;&&&&&&& addEntry(hash, key, value, i);&&&&&&&&&& }&&& // putForNullKey()的作用是将“key为null”键值对添加到table[0]位置&&& private V putForNullKey(V value) {&&&&&&& for (Entry&K,V& e = table[0]; e != e = e.next) {&&&&&&&&&&& if (e.key == null) {&&&&&&&&&&&&&&& V oldValue = e.&&&&&&&&&&&&&&& e.value =&&&&&&&&&&&&&&& e.recordAccess(this);&&&&&&&&&&&&&&& return oldV&&&&&&&&&&& }&&&&&&& }&&&&&&& // 这里的完全不会被执行到!&&&&&&& modCount++;&&&&&&& addEntry(0, null, value, 0);&&&&&&&&&& }&&& // 创建HashMap对应的“添加方法”,&&& // 它和put()不同。putForCreate()是内部方法,它被构造函数等调用,用来创建HashMap&&& // 而put()是对外提供的往HashMap中添加元素的方法。&&& private void putForCreate(K key, V value) {&&&&&&& int hash = (key == null) ? 0 : hash(key.hashCode());&&&&&&& int i = indexFor(hash, table.length);&&&&&&& // 若该HashMap表中存在“键值等于key”的元素,则替换该元素的value值&&&&&&& for (Entry&K,V& e = table[i]; e != e = e.next) {&&&&&&&&&&& O&&&&&&&&&&& if (e.hash == hash &&&&&&&&&&&&&&&&& ((k = e.key) == key || (key != null && key.equals(k)))) {&&&&&&&&&&&&&&& e.value =&&&&&&&&&&&&&&&&&&&&&&&&&& }&&&&&&& }&&&&&&& // 若该HashMap表中不存在“键值等于key”的元素,则将该key-value添加到HashMap中&&&&&&& createEntry(hash, key, value, i);&&& }&&& // 将“m”中的全部元素都添加到HashMap中。&&& // 该方法被内部的构造HashMap的方法所调用。&&& private void putAllForCreate(Map&? extends K, ? extends V& m) {&&&&&&& // 利用迭代器将元素逐个添加到HashMap中&&&&&&& for (Iterator&? extends Map.Entry&? extends K, ? extends V&& i = m.entrySet().iterator(); i.hasNext(); ) {&&&&&&&&&&& Map.Entry&? extends K, ? extends V& e = i.next();&&&&&&&&&&& putForCreate(e.getKey(), e.getValue());&&&&&&& }&&& }&&& // 重新调整HashMap的大小,newCapacity是调整后的单位&&& void resize(int newCapacity) {&&&&&&& Entry[] oldTable =&&&&&&& int oldCapacity = oldTable.&&&&&&& if (oldCapacity == MAXIMUM_CAPACITY) {&&&&&&&&&&& threshold = Integer.MAX_VALUE;&&&&&&&&&&&&&&&&&& }&&&&&&& // 新建一个HashMap,将“旧HashMap”的全部元素添加到“新HashMap”中,&&&&&&& // 然后,将“新HashMap”赋值给“旧HashMap”。&&&&&&& Entry[] newTable = new Entry[newCapacity];&&&&&&& transfer(newTable);&&&&&&& table = newT&&&&&&& threshold = (int)(newCapacity * loadFactor);&&& }&&& // 将HashMap中的全部元素都添加到newTable中&&& void transfer(Entry[] newTable) {&&&&&&& Entry[] src =&&&&&&& int newCapacity = newTable.&&&&&&& for (int j = 0; j & src. j++) {&&&&&&&&&&& Entry&K,V& e = src[j];&&&&&&&&&&& if (e != null) {&&&&&&&&&&&&&&& src[j] =&&&&&&&&&&&&&&& do {&&&&&&&&&&&&&&&&&&& Entry&K,V& next = e.&&&&&&&&&&&&&&&&&&& int i = indexFor(e.hash, newCapacity);&&&&&&&&&&&&&&&&&&& e.next = newTable[i];&&&&&&&&&&&&&&&&&&& newTable[i] =&&&&&&&&&&&&&&&&&&& e =&&&&&&&&&&&&&&& } while (e != null);&&&&&&&&&&& }&&&&&&& }&&& }&&& // 将"m"的全部元素都添加到HashMap中&&& public void putAll(Map&? extends K, ? extends V& m) {&&&&&&& // 有效性判断&&&&&&& int numKeysToBeAdded = m.size();&&&&&&& if (numKeysToBeAdded == 0)&&&&&&&&&&&&&&&&&& // 计算容量是否足够,&&&&&&& // 若“当前实际容量 & 需要的容量”,则将容量x2。&&&&&&& if (numKeysToBeAdded & threshold) {&&&&&&&&&&& int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);&&&&&&&&&&& if (targetCapacity & MAXIMUM_CAPACITY)&&&&&&&&&&&&&&& targetCapacity = MAXIMUM_CAPACITY;&&&&&&&&&&& int newCapacity = table.&&&&&&&&&&& while (newCapacity & targetCapacity)&&&&&&&&&&&&&&& newCapacity &&= 1;&&&&&&&&&&& if (newCapacity & table.length)&&&&&&&&&&&&&&& resize(newCapacity);&&&&&&& }&&&&&&& // 通过迭代器,将“m”中的元素逐个添加到HashMap中。&&&&&&& for (Iterator&? extends Map.Entry&? extends K, ? extends V&& i = m.entrySet().iterator(); i.hasNext(); ) {&&&&&&&&&&& Map.Entry&? extends K, ? extends V& e = i.next();&&&&&&&&&&& put(e.getKey(), e.getValue());&&&&&&& }&&& }&&& // 删除“键为key”元素&&& public V remove(Object key) {&&&&&&& Entry&K,V& e = removeEntryForKey(key);&&&&&&& return (e == null ? null : e.value);&&& }&&& // 删除“键为key”的元素&&& final Entry&K,V& removeEntryForKey(Object key) {&&&&&&& // 获取哈希值。若key为null,则哈希值为0;否则调用hash()进行计算&&&&&&& int hash = (key == null) ? 0 : hash(key.hashCode());&&&&&&& int i = indexFor(hash, table.length);&&&&&&& Entry&K,V& prev = table[i];&&&&&&& Entry&K,V& e =&&&&&&& // 删除链表中“键为key”的元素&&&&&&& // 本质是“删除单向链表中的节点”&&&&&&& while (e != null) {&&&&&&&&&&& Entry&K,V& next = e.&&&&&&&&&&& O&&&&&&&&&&& if (e.hash == hash &&&&&&&&&&&&&&&&& ((k = e.key) == key || (key != null && key.equals(k)))) {&&&&&&&&&&&&&&& modCount++;&&&&&&&&&&&&&&& size--;&&&&&&&&&&&&&&& if (prev == e)&&&&&&&&&&&&&&&&&&& table[i] =&&&&&&&&&&&&&&& else&&&&&&&&&&&&&&&&&&& prev.next =&&&&&&&&&&&&&&& e.recordRemoval(this);&&&&&&&&&&&&&&&&&&&&&&&&&& }&&&&&&&&&&& prev =&&&&&&&&&&& e =&&&&&&& }&&&&&&&&&& }&&& // 删除“键值对”&&& final Entry&K,V& removeMapping(Object o) {&&&&&&& if (!(o instanceof Map.Entry))&&&&&&&&&&&&&&&&&& Map.Entry&K,V& entry = (Map.Entry&K,V&)&&&&&&& Object key = entry.getKey();&&&&&&& int hash = (key == null) ? 0 : hash(key.hashCode());&&&&&&& int i = indexFor(hash, table.length);&&&&&&& Entry&K,V& prev = table[i];&&&&&&& Entry&K,V& e =&&&&&&& // 删除链表中的“键值对e”&&&&&&& // 本质是“删除单向链表中的节点”&&&&&&& while (e != null) {&&&&&&&&&&& Entry&K,V& next = e.&&&&&&&&&&& if (e.hash == hash && e.equals(entry)) {&&&&&&&&&&&&&&& modCount++;&&&&&&&&&&&&&&& size--;&&&&&&&&&&&&&&& if (prev == e)&&&&&&&&&&&&&&&&&&& table[i] =&&&&&&&&&&&&&&& else&&&&&&&&&&&&&&&&&&& prev.next =&&&&&&&&&&&&&&& e.recordRemoval(this);&&&&&&&&&&&&&&&&&&&&&&&&&& }&&&&&&&&&&& prev =&&&&&&&&&&& e =&&&&&&& }&&&&&&&&&& }&&& // 清空HashMap,将所有的元素设为null&&& public void clear() {&&&&&&& modCount++;&&&&&&& Entry[] tab =&&&&&&& for (int i = 0; i & tab. i++)&&&&&&&&&&& tab[i] =&&&&&&& size = 0;&&& }&&& // 是否包含“值为value”的元素&&& public boolean containsValue(Object value) {&&& // 若“value为null”,则调用containsNullValue()查找&&& if (value == null)&&&&&&&&&&& return containsNullValue();&&& // 若“value不为null”,则查找HashMap中是否有值为value的节点。&&& Entry[] tab =&&&&&&& for (int i = 0; i & tab. i++)&&&&&&&&&&& for (Entry e = tab[i] ; e != e = e.next)&&&&&&&&&&&&&&& if (value.equals(e.value))&&&&&&&&&&&&&&&&&&&&&&&&& }&&& // 是否包含null值&&& private boolean containsNullValue() {&&& Entry[] tab =&&&&&&& for (int i = 0; i & tab. i++)&&&&&&&&&&& for (Entry e = tab[i] ; e != e = e.next)&&&&&&&&&&&&&&& if (e.value == null)&&&&&&&&&&&&&&&&&&&&&&&&& }&&& // 克隆一个HashMap,并返回Object对象&&& public Object clone() {&&&&&&& HashMap&K,V& result =&&&&&&& try {&&&&&&&&&&& result = (HashMap&K,V&)super.clone();&&&&&&& } catch (CloneNotSupportedException e) {&&&&&&&&&&& //&&&&&&& }&&&&&&& result.table = new Entry[table.length];&&&&&&& result.entrySet =&&&&&&& result.modCount = 0;&&&&&&& result.size = 0;&&&&&&& result.init();&&&&&&& // 调用putAllForCreate()将全部元素添加到HashMap中&&&&&&& result.putAllForCreate(this);&&&&&&&&&& }&&& // Entry是单向链表。&&& // 它是 “HashMap链式存储法”对应的链表。&&& // 它实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数&&& static class Entry&K,V& implements Map.Entry&K,V& {&&&&&&& final K&&&&&&& V&&&&&&& // 指向下一个节点&&&&&&& Entry&K,V&&&&&&&&&&&&&&& // 构造函数。&&&&&&& // 输入参数包括"哈希值(h)", "键(k)", "值(v)", "下一节点(n)"&&&&&&& Entry(int h, K k, V v, Entry&K,V& n) {&&&&&&&&&&& value =&&&&&&&&&&& next =&&&&&&&&&&& key =&&&&&&&&&&& hash =&&&&&&& }&&&&&&& public final K getKey() {&&&&&&&&&&&&&&&&&& }&&&&&&& public final V getValue() {&&&&&&&&&&&&&&&&&& }&&&&&&& public final V setValue(V newValue) {&&&&&&&&&&& V oldValue =&&&&&&&&&&& value = newV&&&&&&&&&&& return oldV&&&&&&& }&&&&&&& // 判断两个Entry是否相等&&&&&&& // 若两个Entry的“key”和“value”都相等,则返回true。&&&&&&& // 否则,返回false&&&&&&& public final boolean equals(Object o) {&&&&&&&&&&& if (!(o instanceof Map.Entry))&&&&&&&&&&&&&&&&&&&&&&&&&& Map.Entry e = (Map.Entry)o;&&&&&&&&&&& Object k1 = getKey();&&&&&&&&&&& Object k2 = e.getKey();&&&&&&&&&&& if (k1 == k2 || (k1 != null && k1.equals(k2))) {&&&&&&&&&&&&&&& Object v1 = getValue();&&&&&&&&&&&&&&& Object v2 = e.getValue();&&&&&&&&&&&&&&& if (v1 == v2 || (v1 != null && v1.equals(v2)))&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& }&&&&&&&&&&&&&&&&&& }&&&&&&& // 实现hashCode()&&&&&&& public final int hashCode() {&&&&&&&&&&& return (key==null&& ? 0 : key.hashCode()) ^&&&&&&&&&&&&&&&&&& (value==null ? 0 : value.hashCode());&&&&&&& }&&&&&&& public final String toString() {&&&&&&&&&&& return getKey() + "=" + getValue();&&&&&&& }&&&&&&& // 当向HashMap中添加元素时,绘调用recordAccess()。&&&&&&& // 这里不做任何处理&&&&&&& void recordAccess(HashMap&K,V& m) {&&&&&&& }&&&&&&& // 当从HashMap中删除元素时,绘调用recordRemoval()。&&&&&&& // 这里不做任何处理&&&&&&& void recordRemoval(HashMap&K,V& m) {&&&&&&& }&&& }&&& // 新增Entry。将“key-value”插入指定位置,bucketIndex是位置索引。&&& void addEntry(int hash, K key, V value, int bucketIndex) {&&&&&&& // 保存“bucketIndex”位置的值到“e”中&&&&&&& Entry&K,V& e = table[bucketIndex];&&&&&&& // 设置“bucketIndex”位置的元素为“新Entry”,&&&&&&& // 设置“e”为“新Entry的下一个节点”&&&&&&& table[bucketIndex] = new Entry&K,V&(hash, key, value, e);&&&&&&& // 若HashMap的实际大小 不小于 “阈值”,则调整HashMap的大小&&&&&&& if (size++ &= threshold)&&&&&&&&&&& resize(2 * table.length);&&& }&&& // 创建Entry。将“key-value”插入指定位置,bucketIndex是位置索引。&&& // 它和addEntry的区别是:&&& // (01) addEntry()一般用在 新增Entry可能导致“HashMap的实际容量”超过“阈值”的情况下。&&& //&& 例如,我们新建一个HashMap,然后不断通过put()向HashMap中添加元素;&&& // put()是通过addEntry()新增Entry的。&&& //&& 在这种情况下,我们不知道何时“HashMap的实际容量”会超过“阈值”;&&& //&& 因此,需要调用addEntry()&&& // (02) createEntry() 一般用在 新增Entry不会导致“HashMap的实际容量”超过“阈值”的情况下。&&& //&& 例如,我们调用HashMap“带有Map”的构造函数,它绘将Map的全部元素添加到HashMap中;&&& // 但在添加之前,我们已经计算好“HashMap的容量和阈值”。也就是,可以确定“即使将Map中&&& // 的全部元素添加到HashMap中,都不会超过HashMap的阈值”。&&& //&& 此时,调用createEntry()即可。&&& void createEntry(int hash, K key, V value, int bucketIndex) {&&&&&&& // 保存“bucketIndex”位置的值到“e”中&&&&&&& Entry&K,V& e = table[bucketIndex];&&&&&&& // 设置“bucketIndex”位置的元素为“新Entry”,&&&&&&& // 设置“e”为“新Entry的下一个节点”&&&&&&& table[bucketIndex] = new Entry&K,V&(hash, key, value, e);&&&&&&& size++;&&& }&&& // HashIterator是HashMap迭代器的抽象出来的父类,实现了公共了函数。&&& // 它包含“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3个子类。&&& private abstract class HashIterator&E& implements Iterator&E& {&&&&&&& // 下一个元素&&&&&&& Entry&K,V&&&&&&&& // expectedModCount用于实现fast-fail机制。&&&&&&& int expectedModC&&&&&&& // 当前索引&&&&&&&&&&&&&& // 当前元素&&&&&&& Entry&K,V&&&&&&&& HashIterator() {&&&&&&&&&&& expectedModCount = modC&&&&&&&&&&& if (size & 0) { // advance to first entry&&&&&&&&&&&&&&& Entry[] t =&&&&&&&&&&&&&&& // 将next指向table中第一个不为null的元素。&&&&&&&&&&&&&&& // 这里利用了index的初始值为0,从0开始依次向后遍历,直到找到不为null的元素就退出循环。&&&&&&&&&&&&&&& while (index & t.length && (next = t[index++]) == null)&&&&&&&&&&& }&&&&&&& }&&&&&&& public final boolean hasNext() {&&&&&&&&&&& return next !=&&&&&&& }&&&&&&& // 获取下一个元素&&&&&&& final Entry&K,V& nextEntry() {&&&&&&&&&&& if (modCount != expectedModCount)&&&&&&&&&&&&&&& throw new ConcurrentModificationException();&&&&&&&&&&& Entry&K,V& e =&&&&&&&&&&& if (e == null)&&&&&&&&&&&&&&& throw new NoSuchElementException();&&&&&&&&&&& // 注意!!!&&&&&&&&&&& // 一个Entry就是一个单向链表&&&&&&&&&&& // 若该Entry的下一个节点不为空,就将next指向下一个节点;&&&&&&&&&&& // 否则,将next指向下一个链表(也是下一个Entry)的不为null的节点。&&&&&&&&&&& if ((next = e.next) == null) {&&&&&&&&&&&&&&& Entry[] t =&&&&&&&&&&&&&&& while (index & t.length && (next = t[index++]) == null)&&&&&&&&&&& }&&&&&&&&&&& current =&&&&&&&&&&&&&&&&&& }&&&&&&& // 删除当前元素&&&&&&& public void remove() {&&&&&&&&&&& if (current == null)&&&&&&&&&&&&&&& throw new IllegalStateException();&&&&&&&&&&& if (modCount != expectedModCount)&&&&&&&&&&&&&&& throw new ConcurrentModificationException();&&&&&&&&&&& Object k = current.&&&&&&&&&&& current =&&&&&&&&&&& HashMap.this.removeEntryForKey(k);&&&&&&&&&&& expectedModCount = modC&&&&&&& }&&& }&&& // value的迭代器&&& private final class ValueIterator extends HashIterator&V& {&&&&&&& public V next() {&&&&&&&&&&& return nextEntry().&&&&&&& }&&& }&&& // key的迭代器&&& private final class KeyIterator extends HashIterator&K& {&&&&&&& public K next() {&&&&&&&&&&& return nextEntry().getKey();&&&&&&& }&&& }&&& // Entry的迭代器&&& private final class EntryIterator extends HashIterator&Map.Entry&K,V&& {&&&&&&& public Map.Entry&K,V& next() {&&&&&&&&&&& return nextEntry();&&&&&&& }&&& }&&& // 返回一个“key迭代器”&&& Iterator&K& newKeyIterator()&& {&&&&&&& return new KeyIterator();&&& }&&& // 返回一个“value迭代器”&&& Iterator&V& newValueIterator()&& {&&&&&&& return new ValueIterator();&&& }&&& // 返回一个“entry迭代器”&&& Iterator&Map.Entry&K,V&& newEntryIterator()&& {&&&&&&& return new EntryIterator();&&& }&&& // HashMap的Entry对应的集合&&& private transient Set&Map.Entry&K,V&& entrySet =&&& // 返回“key的集合”,实际上返回一个“KeySet对象”&&& public Set&K& keySet() {&&&&&&& Set&K& ks = keyS&&&&&&& return (ks != null ? ks : (keySet = new KeySet()));&&& }&&& // Key对应的集合&&& // KeySet继承于AbstractSet,说明该集合中没有重复的Key。&&& private final class KeySet extends AbstractSet&K& {&&&&&&& public Iterator&K& iterator() {&&&&&&&&&&& return newKeyIterator();&&&&&&& }&&&&&&& public int size() {&&&&&&&&&&&&&&&&&& }&&&&&&& public boolean contains(Object o) {&&&&&&&&&&& return containsKey(o);&&&&&&& }&&&&&&& public boolean remove(Object o) {&&&&&&&&&&& return HashMap.this.removeEntryForKey(o) !=&&&&&&& }&&&&&&& public void clear() {&&&&&&&&&&& HashMap.this.clear();&&&&&&& }&&& }&&& // 返回“value集合”,实际上返回的是一个Values对象&&& public Collection&V& values() {&&&&&&& Collection&V& vs =&&&&&&& return (vs != null ? vs : (values = new Values()));&&& }&&& // “value集合”&&& // Values继承于AbstractCollection,不同于“KeySet继承于AbstractSet”,&&& // Values中的元素能够重复。因为不同的key可以指向相同的value。&&& private final class Values extends AbstractCollection&V& {&&&&&&& public Iterator&V& iterator() {&&&&&&&&&&& return newValueIterator();&&&&&&& }&&&&&&& public int size() {&&&&&&&&&&&&&&&&&& }&&&&&&& public boolean contains(Object o) {&&&&&&&&&&& return containsValue(o);&&&&&&& }&&&&&&& public void clear() {&&&&&&&&&&& HashMap.this.clear();&&&&&&& }&&& }&&& // 返回“HashMap的Entry集合”&&& public Set&Map.Entry&K,V&& entrySet() {&&&&&&& return entrySet0();&&& }&&& // 返回“HashMap的Entry集合”,它实际是返回一个EntrySet对象&&& private Set&Map.Entry&K,V&& entrySet0() {&&&&&&& Set&Map.Entry&K,V&& es = entryS&&&&&&& return es != null ? es : (entrySet = new EntrySet());&&& }&&& // EntrySet对应的集合&&& // EntrySet继承于AbstractSet,说明该集合中没有重复的EntrySet。&&& private final class EntrySet extends AbstractSet&Map.Entry&K,V&& {&&&&&&& public Iterator&Map.Entry&K,V&& iterator() {&&&&&&&&&&& return newEntryIterator();&&&&&&& }&&&&&&& public boolean contains(Object o) {&&&&&&&&&&& if (!(o instanceof Map.Entry))&&&&&&&&&&&&&&&&&&&&&&&&&& Map.Entry&K,V& e = (Map.Entry&K,V&)&&&&&&&&&&& Entry&K,V& candidate = getEntry(e.getKey());&&&&&&&&&&& return candidate != null && candidate.equals(e);&&&&&&& }&&&&&&& public boolean remove(Object o) {&&&&&&&&&&& return removeMapping(o) !=&&&&&&& }&&&&&&& public int size() {&&&&&&&&&&&&&&&&&& }&&&&&&& public void clear() {&&&&&&&&&&& HashMap.this.clear();&&&&&&& }&&& }&&& // java.io.Serializable的写入函数&&& // 将HashMap的“总的容量,实际容量,所有的Entry”都写入到输出流中&&& private void writeObject(java.io.ObjectOutputStream s)&&&&&&& throws IOException&&& {&&&&&&& Iterator&Map.Entry&K,V&& i =&&&&&&&&&&& (size & 0) ? entrySet0().iterator() :&&&&&&& // Write out the threshold, loadfactor, and any hidden stuff&&&&&&& s.defaultWriteObject();&&&&&&& // Write out number of buckets&&&&&&& s.writeInt(table.length);&&&&&&& // Write out size (number of Mappings)&&&&&&& s.writeInt(size);&&&&&&& // Write out keys and values (alternating)&&&&&&& if (i != null) {&&&&&&&&&&& while (i.hasNext()) {&&&&&&&&&&& Map.Entry&K,V& e = i.next();&&&&&&&&&&& s.writeObject(e.getKey());&&&&&&&&&&& s.writeObject(e.getValue());&&&&&&&&&&& }&&&&&&& }&&& }
&&& private static final long serialVersionUID = 181265L;&&& // java.io.Serializable的读取函数:根据写入方式读出&&& // 将HashMap的“总的容量,实际容量,所有的Entry”依次读出&&& private void readObject(java.io.ObjectInputStream s)&&&&&&&& throws IOException, ClassNotFoundException&&& {&&&&&&& // Read in the threshold, loadfactor, and any hidden stuff&&&&&&& s.defaultReadObject();&&&&&&& // Read in number of buckets and alloc&&&&&&& int numBuckets = s.readInt();&&&&&&& table = new Entry[numBuckets];&&&&&&& init();& // Give subclass a chance to do its thing.&&&&&&& // Read in size (number of Mappings)&&&&&&& int size = s.readInt();&&&&&&& // Read the keys and values, and put the mappings in the HashMap&&&&&&& for (int i=0; i& i++) {&&&&&&&&&&& K key = (K) s.readObject();&&&&&&&&&&& V value = (V) s.readObject();&&&&&&&&&&& putForCreate(key, value);&&&&&&& }&&& }&&& // 返回“HashMap总的容量”&&& int&& capacity()&&&& { return table. }&&& // 返回“HashMap的加载因子”&&& float loadFactor()&& { return loadF&& }}说明:在详细介绍HashMap的代码之前,我们需要了解:HashMap就是一个散列表,它是通过“拉链法”解决哈希冲突的。还需要再补充说明的一点是影响HashMap性能的有两个参数:初始容量(initialCapacity) 和加载因子(loadFactor)。容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。第2.1部分 HashMap的“拉链法”相关内容2.1.1 HashMap数据存储数组transient Entry[]HashMap中的key-value都是存储在Entry数组中的。2.1.2 数据节点Entry的数据结构 代码如下:static class Entry&K,V& implements Map.Entry&K,V& {&&& final K&&& V&&& // 指向下一个节点&&& Entry&K,V&&&&&&& // 构造函数。&&& // 输入参数包括"哈希值(h)", "键(k)", "值(v)", "下一节点(n)"&&& Entry(int h, K k, V v, Entry&K,V& n) {&&&&&&& value =&&&&&&& next =&&&&&&& key =&&&&&&& hash =&&& }&&& public final K getKey() {&&&&&&&&&& }&&& public final V getValue() {&&&&&&&&&& }&&& public final V setValue(V newValue) {&&&&&&& V oldValue =&&&&&&& value = newV&&&&&&& return oldV&&& }&&& // 判断两个Entry是否相等&&& // 若两个Entry的“key”和“value”都相等,则返回true。&&& // 否则,返回false&&& public final boolean equals(Object o) {&&&&&&& if (!(o instanceof Map.Entry))&&&&&&&&&&&&&&&&&& Map.Entry e = (Map.Entry)o;&&&&&&& Object k1 = getKey();&&&&&&& Object k2 = e.getKey();&&&&&&& if (k1 == k2 || (k1 != null && k1.equals(k2))) {&&&&&&&&&&& Object v1 = getValue();&&&&&&&&&&& Object v2 = e.getValue();&&&&&&&&&&& if (v1 == v2 || (v1 != null && v1.equals(v2)))&&&&&&&&&&&&&&&&&&&&&& }&&&&&&&&&& }&&& // 实现hashCode()&&& public final int hashCode() {&&&&&&& return (key==null&& ? 0 : key.hashCode()) ^&&&&&&&&&&&&&& (value==null ? 0 : value.hashCode());&&& }&&& public final String toString() {&&&&&&& return getKey() + "=" + getValue();&&& }&&& // 当向HashMap中添加元素时,绘调用recordAccess()。&&& // 这里不做任何处理&&& void recordAccess(HashMap&K,V& m) {&&& }&&& // 当从HashMap中删除元素时,绘调用recordRemoval()。&&& // 这里不做任何处理&&& void recordRemoval(HashMap&K,V& m) {&&& }}从中,我们可以看出 Entry 实际上就是一个单向链表。这也是为什么我们说HashMap是通过拉链法解决哈希冲突的。Entry 实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数。这些都是基本的读取/修改key、value值的函数。第2.2部分 HashMap的构造函数HashMap共包括4个构造函数 代码如下:// 默认构造函数。public HashMap() {&&& // 设置“加载因子”&&& this.loadFactor = DEFAULT_LOAD_FACTOR;&&& // 设置“HashMap阈值”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。&&& threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);&&& // 创建Entry数组,用来保存数据&&& table = new Entry[DEFAULT_INITIAL_CAPACITY];&&& init();}// 指定“容量大小”和“加载因子”的构造函数public HashMap(int initialCapacity, float loadFactor) {&&& if (initialCapacity & 0)&&&&&&& throw new IllegalArgumentException("Illegal initial capacity: " +&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& initialCapacity);&&& // HashMap的最大容量只能是MAXIMUM_CAPACITY&&& if (initialCapacity & MAXIMUM_CAPACITY)&&&&&&& initialCapacity = MAXIMUM_CAPACITY;&&& if (loadFactor &= 0 || Float.isNaN(loadFactor))&&&&&&& throw new IllegalArgumentException("Illegal load factor: " +&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& loadFactor);&&& // Find a power of 2 &= initialCapacity&&& int capacity = 1;&&& while (capacity & initialCapacity)&&&&&&& capacity &&= 1;&&& // 设置“加载因子”&&& this.loadFactor = loadF&&& // 设置“HashMap阈值”,当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。&&& threshold = (int)(capacity * loadFactor);&&& // 创建Entry数组,用来保存数据&&& table = new Entry[capacity];&&& init();}// 指定“容量大小”的构造函数public HashMap(int initialCapacity) {&&& this(initialCapacity, DEFAULT_LOAD_FACTOR);}// 包含“子Map”的构造函数public HashMap(Map&? extends K, ? extends V& m) {&&& this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,&&&&&&&&&&&&&&&&& DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);&&& // 将m中的全部元素逐个添加到HashMap中&&& putAllForCreate(m);}第2.3部分 HashMap的主要对外接口2.3.1 clear()clear() 的作用是清空HashMap。它是通过将所有的元素设为null来实现的。 代码如下:public void clear() {&&& modCount++;&&& Entry[] tab =&&& for (int i = 0; i & tab. i++)&&&&&&& tab[i] =&&& size = 0;}2.3.2 containsKey()containsKey() 的作用是判断HashMap是否包含key。 代码如下:public boolean containsKey(Object key) {&&& return getEntry(key) !=}containsKey() 首先通过getEntry(key)获取key对应的Entry,然后判断该Entry是否为null。getEntry()的源码如下: 代码如下:final Entry&K,V& getEntry(Object key) {&&& // 获取哈希值&&& // HashMap将“key为null”的元素存储在table[0]位置,“key不为null”的则调用hash()计算哈希值&&& int hash = (key == null) ? 0 : hash(key.hashCode());&&& // 在“该hash值对应的链表”上查找“键值等于key”的元素&&& for (Entry&K,V& e = table[indexFor(hash, table.length)];&&&&&&&& e !=&&&&&&&& e = e.next) {&&&&&&& O&&&&&&& if (e.hash == hash &&&&&&&&&&&&& ((k = e.key) == key || (key != null && key.equals(k))))&&&&&&&&&&&&&& }&&&}getEntry() 的作用就是返回“键为key”的键值对,它的实现源码中已经进行了说明。这里需要强调的是:HashMap将“key为null”的元素都放在table的位置0处,即table[0]中;“key不为null”的放在table的其余位置!2.3.3 containsValue()containsValue() 的作用是判断HashMap是否包含“值为value”的元素。 代码如下:public boolean containsValue(Object value) {&&& // 若“value为null”,则调用containsNullValue()查找&&& if (value == null)&&&&&&& return containsNullValue();&&& // 若“value不为null”,则查找HashMap中是否有值为value的节点。&&& Entry[] tab =&&& for (int i = 0; i & tab. i++)&&&&&&& for (Entry e = tab[i] ; e != e = e.next)&&&&&&&&&&& if (value.equals(e.value))&&&&&&&&&&&&&&&&&&}从中,我们可以看出containsNullValue()分为两步进行处理:第一,若“value为null”,则调用containsNullValue()。第二,若“value不为null”,则查找HashMap中是否有值为value的节点。containsNullValue() 的作用判断HashMap中是否包含“值为null”的元素。 代码如下:private boolean containsNullValue() {&&& Entry[] tab =&&& for (int i = 0; i & tab. i++)&&&&&&& for (Entry e = tab[i] ; e != e = e.next)&&&&&&&&&&& if (e.value == null)&&&&&&&&&&&&&&&&&&}2.3.4 entrySet()、values()、keySet()它们3个的原理类似,这里以entrySet()为例来说明。entrySet()的作用是返回“HashMap中所有Entry的集合”,它是一个集合。实现代码如下: 代码如下:// 返回“HashMap的Entry集合”public Set&Map.Entry&K,V&& entrySet() {&&& return entrySet0();}// 返回“HashMap的Entry集合”,它实际是返回一个EntrySet对象private Set&Map.Entry&K,V&& entrySet0() {&&& Set&Map.Entry&K,V&& es = entryS&&& return es != null ? es : (entrySet = new EntrySet());}// EntrySet对应的集合// EntrySet继承于AbstractSet,说明该集合中没有重复的EntrySet。private final class EntrySet extends AbstractSet&Map.Entry&K,V&& {&&& public Iterator&Map.Entry&K,V&& iterator() {&&&&&&& return newEntryIterator();&&& }&&& public boolean contains(Object o) {&&&&&&& if (!(o instanceof Map.Entry))&&&&&&&&&&&&&&&&&& Map.Entry&K,V& e = (Map.Entry&K,V&)&&&&&&& Entry&K,V& candidate = getEntry(e.getKey());&&&&&&& return candidate != null && candidate.equals(e);&&& }&&& public boolean remove(Object o) {&&&&&&& return removeMapping(o) !=&&& }&&& public int size() {&&&&&&&&&& }&&& public void clear() {&&&&&&& HashMap.this.clear();&&& }}HashMap是通过拉链法实现的散列表。表现在HashMap包括许多的Entry,而每一个Entry本质上又是一个单向链表。那么HashMap遍历key-value键值对的时候,是如何逐个去遍历的呢?
下面我们就看看HashMap是如何通过entrySet()遍历的。entrySet()实际上是通过newEntryIterator()实现的。 下面我们看看它的代码: 代码如下:// 返回一个“entry迭代器”Iterator&Map.Entry&K,V&& newEntryIterator()&& {&&& return new EntryIterator();}// Entry的迭代器private final class EntryIterator extends HashIterator&Map.Entry&K,V&& {&&& public Map.Entry&K,V& next() {&&&&&&& return nextEntry();&&& }}// HashIterator是HashMap迭代器的抽象出来的父类,实现了公共了函数。// 它包含“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3个子类。private abstract class HashIterator&E& implements Iterator&E& {&&& // 下一个元素&&& Entry&K,V&&&& // expectedModCount用于实现fast-fail机制。&&& int expectedModC&&& // 当前索引&&&&&& // 当前元素&&& Entry&K,V&&&& HashIterator() {&&&&&&& expectedModCount = modC&&&&&&& if (size & 0) { // advance to first entry&&&&&&&&&&& Entry[] t =&&&&&&&&&&& // 将next指向table中第一个不为null的元素。&&&&&&&&&&& // 这里利用了index的初始值为0,从0开始依次向后遍历,直到找到不为null的元素就退出循环。&&&&&&&&&&& while (index & t.length && (next = t[index++]) == null)&&&&&&& }&&& }&&& public final boolean hasNext() {&&&&&&& return next !=&&& }&&& // 获取下一个元素&&& final Entry&K,V& nextEntry() {&&&&&&& if (modCount != expectedModCount)&&&&&&&&&&& throw new ConcurrentModificationException();&&&&&&& Entry&K,V& e =&&&&&&& if (e == null)&&&&&&&&&&& throw new NoSuchElementException();&&&&&&& // 注意!!!&&&&&&& // 一个Entry就是一个单向链表&&&&&&& // 若该Entry的下一个节点不为空,就将next指向下一个节点;&&&&&&& // 否则,将next指向下一个链表(也是下一个Entry)的不为null的节点。&&&&&&& if ((next = e.next) == null) {&&&&&&&&&&& Entry[] t =&&&&&&&&&&& while (index & t.length && (next = t[index++]) == null)&&&&&&& }&&&&&&& current =&&&&&&&&&& }&&& // 删除当前元素&&& public void remove() {&&&&&&& if (current == null)&&&&&&&&&&& throw new IllegalStateException();&&&&&&& if (modCount != expectedModCount)&&&&&&&&&&& throw new ConcurrentModificationException();&&&&&&& Object k = current.&&&&&&& current =&&&&&&& HashMap.this.removeEntryForKey(k);&&&&&&& expectedModCount = modC&&& }}当我们通过entrySet()获取到的Iterator的next()方法去遍历HashMap时,实际上调用的是 nextEntry() 。而nextEntry()的实现方式,先遍历Entry(根据Entry在table中的序号,从小到大的遍历);然后对每个Entry(即每个单向链表),逐个遍历。2.3.5 get()get() 的作用是获取key对应的value,它的实现代码如下: 代码如下:public V get(Object key) {&&& if (key == null)&&&&&&& return getForNullKey();&&& // 获取key的hash值&&& int hash = hash(key.hashCode());&&& // 在“该hash值对应的链表”上查找“键值等于key”的元素&&& for (Entry&K,V& e = table[indexFor(hash, table.length)];&&&&&&&& e !=&&&&&&&& e = e.next) {&&&&&&& O&&&&&&& if (e.hash == hash && ((k = e.key) == key || key.equals(k)))&&&&&&&&&&& return e.&&& }&&&}2.3.6 put()put() 的作用是对外提供接口,让HashMap对象可以通过put()将“key-value”添加到HashMap中。 代码如下:public V put(K key, V value) {&&& // 若“key为null”,则将该键值对添加到table[0]中。&&& if (key == null)&&&&&&& return putForNullKey(value);&&& // 若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。&&& int hash = hash(key.hashCode());&&& int i = indexFor(hash, table.length);&&& for (Entry&K,V& e = table[i]; e != e = e.next) {&&&&&&& O&&&&&&& // 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!&&&&&&& if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {&&&&&&&&&&& V oldValue = e.&&&&&&&&&&& e.value =&&&&&&&&&&& e.recordAccess(this);&&&&&&&&&&& return oldV&&&&&&& }&&& }&&& // 若“该key”对应的键值对不存在,则将“key-value”添加到table中&&& modCount++;&&& addEntry(hash, key, value, i);&&&}若要添加到HashMap中的键值对对应的key已经存在HashMap中,则找到该键值对;然后新的value取代旧的value,并退出!若要添加到HashMap中的键值对对应的key不在HashMap中,则将其添加到该哈希值对应的链表中,并调用addEntry()。下面看看addEntry()的代码: 代码如下:void addEntry(int hash, K key, V value, int bucketIndex) {&&& // 保存“bucketIndex”位置的值到“e”中&&& Entry&K,V& e = table[bucketIndex];&&& // 设置“bucketIndex”位置的元素为“新Entry”,&&& // 设置“e”为“新Entry的下一个节点”&&& table[bucketIndex] = new Entry&K,V&(hash, key, value, e);&&& // 若HashMap的实际大小 不小于 “阈值”,则调整HashMap的大小&&& if (size++ &= threshold)&&&&&&& resize(2 * table.length);}addEntry() 的作用是新增Entry。将“key-value”插入指定位置,bucketIndex是位置索引。说到addEntry(),就不得不说另一个函数createEntry()。createEntry()的代码如下: 代码如下:void createEntry(int hash, K key, V value, int bucketIndex) {&&& // 保存“bucketIndex”位置的值到“e”中&&& Entry&K,V& e = table[bucketIndex];&&& // 设置“bucketIndex”位置的元素为“新Entry”,&&& // 设置“e”为“新Entry的下一个节点”&&& table[bucketIndex] = new Entry&K,V&(hash, key, value, e);&&& size++;}它们的作用都是将key、value添加到HashMap中。而且,比较addEntry()和createEntry()的代码,我们发现addEntry()多了两句:if (size++ &= threshold)&&& resize(2 * table.length);那它们的区别到底是什么呢?阅读代码,我们可以发现,它们的使用情景不同。(01) addEntry()一般用在 新增Entry可能导致“HashMap的实际容量”超过“阈值”的情况下。&&&&&& 例如,我们新建一个HashMap,然后不断通过put()向HashMap中添加元素;put()是通过addEntry()新增Entry的。&&&&&& 在这种情况下,我们不知道何时“HashMap的实际容量”会超过“阈值”;&&&&&& 因此,需要调用addEntry()(02) createEntry() 一般用在 新增Entry不会导致“HashMap的实际容量”超过“阈值”的情况下。&&&&&&& 例如,我们调用HashMap“带有Map”的构造函数,它绘将Map的全部元素添加到HashMap中;&&&&&& 但在添加之前,我们已经计算好“HashMap的容量和阈值”。也就是,可以确定“即使将Map中的全部元素添加到HashMap中,都不会超过HashMap的阈值”。&&&&&& 此时,调用createEntry()即可。2.3.7 putAll()putAll() 的作用是将"m"的全部元素都添加到HashMap中,它的代码如下: 代码如下:public void putAll(Map&? extends K, ? extends V& m) {&&& // 有效性判断&&& int numKeysToBeAdded = m.size();&&& if (numKeysToBeAdded == 0)&&&&&&&&&& // 计算容量是否足够,&&& // 若“当前实际容量 & 需要的容量”,则将容量x2。&&& if (numKeysToBeAdded & threshold) {&&&&&&& int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);&&&&&&& if (targetCapacity & MAXIMUM_CAPACITY)&&&&&&&&&&& targetCapacity = MAXIMUM_CAPACITY;&&&&&&& int newCapacity = table.&&&&&&& while (newCapacity & targetCapacity)&&&&&&&&&&& newCapacity &&= 1;&&&&&&& if (newCapacity & table.length)&&&&&&&&&&& resize(newCapacity);&&& }&&& // 通过迭代器,将“m”中的元素逐个添加到HashMap中。&&& for (Iterator&? extends Map.Entry&? extends K, ? extends V&& i = m.entrySet().iterator(); i.hasNext(); ) {&&&&&&& Map.Entry&? extends K, ? extends V& e = i.next();&&&&&&& put(e.getKey(), e.getValue());&&& }}2.3.8 remove()remove() 的作用是删除“键为key”元素 代码如下:public V remove(Object key) {&&& Entry&K,V& e = removeEntryForKey(key);&&& return (e == null ? null : e.value);}
// 删除“键为key”的元素final Entry&K,V& removeEntryForKey(Object key) {&&& // 获取哈希值。若key为null,则哈希值为0;否则调用hash()进行计算&&& int hash = (key == null) ? 0 : hash(key.hashCode());&&& int i = indexFor(hash, table.length);&&& Entry&K,V& prev = table[i];&&& Entry&K,V& e =&&& // 删除链表中“键为key”的元素&&& // 本质是“删除单向链表中的节点”&&& while (e != null) {&&&&&&& Entry&K,V& next = e.&&&&&&& O&&&&&&& if (e.hash == hash &&&&&&&&&&&&& ((k = e.key) == key || (key != null && key.equals(k)))) {&&&&&&&&&&& modCount++;&&&&&&&&&&& size--;&&&&&&&&&&& if (prev == e)&&&&&&&&&&&&&&& table[i] =&&&&&&&&&&& else&&&&&&&&&&&&&&& prev.next =&&&&&&&&&&& e.recordRemoval(this);&&&&&&&&&&&&&&&&&& }&&&&&&& prev =&&&&&&& e =&&& }&&&}第2.4部分 HashMap实现的Cloneable接口HashMap实现了Cloneable接口,即实现了clone()方法。clone()方法的作用很简单,就是克隆一个HashMap对象并返回。 代码如下:// 克隆一个HashMap,并返回Object对象public Object clone() {&&& HashMap&K,V& result =&&& try {&&&&&&& result = (HashMap&K,V&)super.clone();&&& } catch (CloneNotSupportedException e) {&&&&&&& //&&& }&&& result.table = new Entry[table.length];&&& result.entrySet =&&& result.modCount = 0;&&& result.size = 0;&&& result.init();&&& // 调用putAllForCreate()将全部元素添加到HashMap中&&& result.putAllForCreate(this);&&&}第2.5部分 HashMap实现的Serializable接口HashMap实现java.io.Serializable,分别实现了串行读取、写入功能。串行写入函数是writeObject(),它的作用是将HashMap的“总的容量,实际容量,所有的Entry”都写入到输出流中。而串行读取函数是readObject(),它的作用是将HashMap的“总的容量,实际容量,所有的Entry”依次读出 代码如下:// java.io.Serializable的写入函数// 将HashMap的“总的容量,实际容量,所有的Entry”都写入到输出流中private void writeObject(java.io.ObjectOutputStream s)&&& throws IOException{&&& Iterator&Map.Entry&K,V&& i =&&&&&&& (size & 0) ? entrySet0().iterator() :&&& // Write out the threshold, loadfactor, and any hidden stuff&&& s.defaultWriteObject();&&& // Write out number of buckets&&& s.writeInt(table.length);&&& // Write out size (number of Mappings)&&& s.writeInt(size);&&& // Write out keys and values (alternating)&&& if (i != null) {&&&&&&& while (i.hasNext()) {&&&&&&& Map.Entry&K,V& e = i.next();&&&&&&& s.writeObject(e.getKey());&&&&&&& s.writeObject(e.getValue());&&&&&&& }&&& }}// java.io.Serializable的读取函数:根据写入方式读出// 将HashMap的“总的容量,实际容量,所有的Entry”依次读出private void readObject(java.io.ObjectInputStream s)&&&& throws IOException, ClassNotFoundException{&&& // Read in the threshold, loadfactor, and any hidden stuff&&& s.defaultReadObject();&&& // Read in number of buckets and alloc&&& int numBuckets = s.readInt();&&& table = new Entry[numBuckets];&&& init();& // Give subclass a chance to do its thing.&&& // Read in size (number of Mappings)&&& int size = s.readInt();&&& // Read the keys and values, and put the mappings in the HashMap&&& for (int i=0; i& i++) {&&&&&&& K key = (K) s.readObject();&&&&&&& V value = (V) s.readObject();&&&&&&& putForCreate(key, value);&&& }}第3部分 HashMap遍历方式3.1 遍历HashMap的键值对第一步:根据entrySet()获取HashMap的“键值对”的Set集合。第二步:通过Iterator迭代器遍历“第一步”得到的集合。 代码如下:// 假设map是HashMap对象// map中的key是String类型,value是Integer类型Integer integ =Iterator iter = map.entrySet().iterator();while(iter.hasNext()) {&&& Map.Entry entry = (Map.Entry)iter.next();&&& // 获取key&&& key = (String)entry.getKey();&&&&&&& // 获取value&&& integ = (Integer)entry.getValue();}3.2 遍历HashMap的键第一步:根据keySet()获取HashMap的“键”的Set集合。第二步:通过Iterator迭代器遍历“第一步”得到的集合。 代码如下:// 假设map是HashMap对象// map中的key是String类型,value是Integer类型String key =Integer integ =Iterator iter = map.keySet().iterator();while (iter.hasNext()) {&&&&&&& // 获取key&&& key = (String)iter.next();&&&&&&& // 根据key,获取value&&& integ = (Integer)map.get(key);}3.3 遍历HashMap的值第一步:根据value()获取HashMap的“值”的集合。第二步:通过Iterator迭代器遍历“第一步”得到的集合。 代码如下:// 假设map是HashMap对象// map中的key是String类型,value是Integer类型Integer value =Collection c = map.values();Iterator iter= c.iterator();while (iter.hasNext()) {&&& value = (Integer)iter.next();}遍历测试程序如下: 代码如下:import java.util.Mimport java.util.Rimport java.util.Iimport java.util.HashMimport java.util.HashSimport java.util.Map.Eimport java.util.C/*&* @desc 遍历HashMap的测试程序。&*&& (01) 通过entrySet()去遍历key、value,参考实现函数:&*&&&&&&& iteratorHashMapByEntryset()&*&& (02) 通过keySet()去遍历key、value,参考实现函数:&*&&&&&&& iteratorHashMapByKeyset()&*&& (03) 通过values()去遍历value,参考实现函数:&*&&&&&&& iteratorHashMapJustValues()&*&* @author skywang&*/public class HashMapIteratorTest {&&& public static void main(String[] args) {&&&&&&& int val = 0;&&&&&&& String key =&&&&&&& Integer value =&&&&&&& Random r = new Random();&&&&&&& HashMap map = new HashMap();&&&&&&& for (int i=0; i&12; i++) {&&&&&&&&&&& // 随机获取一个[0,100)之间的数字&&&&&&&&&&& val = r.nextInt(100);&&&&&&&&&&& key = String.valueOf(val);&&&&&&&&&&& value = r.nextInt(5);&&&&&&&&&&& // 添加到HashMap中&&&&&&&&&&& map.put(key, value);&&&&&&&&&&& System.out.println(" key:"+key+" value:"+value);&&&&&&& }&&&&&&& // 通过entrySet()遍历HashMap的key-value&&&&&&& iteratorHashMapByEntryset(map) ;&&&&&&& // 通过keySet()遍历HashMap的key-value&&&&&&& iteratorHashMapByKeyset(map) ;&&&&&&& // 单单遍历HashMap的value&&&&&&& iteratorHashMapJustValues(map);&&&&&&& &&& }&&& /*&&&& * 通过entry set遍历HashMap&&&& * 效率高!&&&& */&&& private static void iteratorHashMapByEntryset(HashMap map) {&&&&&&& if (map == null)&&&&&&&&&&&&&&&&&& System.out.println("\niterator HashMap By entryset");&&&&&&& String key =&&&&&&& Integer integ =&&&&&&& Iterator iter = map.entrySet().iterator();&&&&&&& while(iter.hasNext()) {&&&&&&&&&&& Map.Entry entry = (Map.Entry)iter.next();&&&&&&&&&&& key = (String)entry.getKey();&&&&&&&&&&& integ = (Integer)entry.getValue();&&&&&&&&&&& System.out.println(key+" -- "+integ.intValue());&&&&&&& }&&& }&&& /*&&&& * 通过keyset来遍历HashMap&&&& * 效率低!&&&& */&&& private static void iteratorHashMapByKeyset(HashMap map) {&&&&&&& if (map == null)&&&&&&&&&&&&&&&&&& System.out.println("\niterator HashMap By keyset");&&&&&&& String key =&&&&&&& Integer integ =&&&&&&& Iterator iter = map.keySet().iterator();&&&&&&& while (iter.hasNext()) {&&&&&&&&&&& key = (String)iter.next();&&&&&&&&&&& integ = (Integer)map.get(key);&&&&&&&&&&& System.out.println(key+" -- "+integ.intValue());&&&&&&& }&&& }&&& /*&&&& * 遍历HashMap的values&&&& */&&& private static void iteratorHashMapJustValues(HashMap map) {&&&&&&& if (map == null)&&&&&&&&&&&&&&&&&& Collection c = map.values();&&&&&&& Iterator iter= c.iterator();&&&&&&& while (iter.hasNext()) {&&&&&&&&&&& System.out.println(iter.next());&&&&&& }&&& }}第4部分 HashMap示例下面通过一个实例学习如何使用HashMapimport java.util.Mimport java.util.Rimport java.util.Iimport java.util.HashMimport java.util.HashSimport java.util.Map.Eimport java.util.C/*&* @desc HashMap测试程序&*&&&&&&& &* @author skywang&*/public class HashMapTest {&&& public static void main(String[] args) {&&&&&&& testHashMapAPIs();&&& }&&& private static void testHashMapAPIs() {&&&&&&& // 初始化随机种子&&&&&&& Random r = new Random();&&&&&&& // 新建HashMap&&&&&&& HashMap map = new HashMap();&&&&&&& // 添加操作&&&&&&& map.put("one", r.nextInt(10));&&&&&&& map.put("two", r.nextInt(10));&&&&&&& map.put("three", r.nextInt(10));&&&&&&& // 打印出map&&&&&&& System.out.println("map:"+map );&&&&&&& // 通过Iterator遍历key-value&&&&&&& Iterator iter = map.entrySet().iterator();&&&&&&& while(iter.hasNext()) {&&&&&&&&&&& Map.Entry entry = (Map.Entry)iter.next();&&&&&&&&&&& System.out.println("next : "+ entry.getKey() +" - "+entry.getValue());&&&&&&& }&&&&&&& // HashMap的键值对个数&&&&&&& &&&&&&& System.out.println("size:"+map.size());&&&&&&& // containsKey(Object key) :是否包含键key&&&&&&& System.out.println("contains key two : "+map.containsKey("two"));&&&&&&& System.out.println("contains key five : "+map.containsKey("five"));&&&&&&& // containsValue(Object value) :是否包含值value&&&&&&& System.out.println("contains value 0 : "+map.containsValue(new Integer(0)));&&&&&&& // remove(Object key) : 删除键key对应的键值对&&&&&&& map.remove("three");&&&&&&& System.out.println("map:"+map );&&&&&&& // clear() : 清空HashMap&&&&&&& map.clear();&&&&&&& // isEmpty() : HashMap是否为空&&&&&&& System.out.println((map.isEmpty()?"map is empty":"map is not empty") );&&& }}
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具}

我要回帖

更多关于 父类引用指向子类对象 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信