diff --git a/source/_posts/Java/HashMap的工作原理.md b/source/_posts/Java/HashMap的工作原理.md new file mode 100644 index 0000000..bf73df2 --- /dev/null +++ b/source/_posts/Java/HashMap的工作原理.md @@ -0,0 +1,94 @@ +--- +title: HashMap的工作原理 +date: 2018-5-14 20:48:49 +categories: + - Java +--- +为了验证HashMap的工作原理 , 先创建一个JavaBean实体类 + +```java +public class Country { + String name; + long population; + + public Country(String name, long population) { + this.name = name; + this.population = population; + } + + @Override + public int hashCode() { + if (this.name.length() % 2 == 0) + return 31; + else + return 95; + } + + @Override + public boolean equals(Object obj) { + + Country other = (Country) obj; + if (name.equalsIgnoreCase(other.name)) + return true; + return false; + } +} +``` +需要注意的是这个类重写了hashCode和equals方法 +hashCode方法在name字符串的长度为奇数和偶数的时候会返回不同的常数值 +equals方法则是忽略大小写比较name属性是否相同 + +调试执行以下代码 +```java +public static void main(String[] args) { + Country india = new Country("India", 1000); + Country japan = new Country("Japan", 10000); + + Country france = new Country("France", 2000); + Country russia = new Country("Russia", 20000); + + HashMap countryCapitalMap = new HashMap(); + countryCapitalMap.put(india, "Delhi"); + countryCapitalMap.put(japan, "Tokyo"); + countryCapitalMap.put(france, "Paris"); + countryCapitalMap.put(russia, "Moscow"); + + Iterator countryCapitalIter = countryCapitalMap.keySet().iterator(); + while (countryCapitalIter.hasNext()) { + Country countryObj = countryCapitalIter.next(); + String capital = countryCapitalMap.get(countryObj); + System.out.println(countryObj.getName() + "----" + capital); + } +} +``` +![HashMap1](/images/Java/HashMap1.png) + +发现这个Map当中存在4个Entry ( 一组键值对构成的对象 ) +但是在table这个存放Entry的数组当中只有两个位置有数据 +这两个位置上又分别使用链表的结构存放了两个Entry +![HashMap2](/images/Java/HashMap2.png) + +由上述现象可以总结出 +HashMap是一个由数组和链表结合构成的复合型的数据结构 +**某一个Entry存放在哪个数组索引上 , 是由该键的hashCode方法的返回值决定的** +如果两个键存在相同的哈希值( 也称为哈希冲突 ) , 那么将保存在同一个索引上面 , 以链表的形式存在 +当向这个链表末尾追加元素时 , 需要对这个链表进行迭代 +**在这个迭代过程中 , 会使用键的equals方法进行比较 , 如果在某个链表节点上获得了true的结果** +**那么新的Entry会替换掉这个原有的链表节点** +**如果遍历该链表后没有发现重复的元素 , 那么该Entry将追加到该链表末尾** + +所以当向Map当中存放数据的时候 , "键"的对象最好是一个不可变的对象 +但是对象本身是否可变并不是问题的关键 +而是该对象是否会产生稳定的哈希值 ( 也就是hashCode方法的返回值 ) +如果某些因素导致了哈希值的变化 , 虽然该元素仍然在Map当中存在 +但是已经无法用get方法拿到其对应的值 + +总结 + +* HashMap有一个叫做Entry的内部类,它用来存储key-value对。 +* 上面的Entry对象是存储在一个叫做table的Entry数组中。 +* table的索引在逻辑上叫做“桶”(bucket),它存储了链表的第一个元素。 +* key的hashcode()方法用来找到Entry对象所在的桶。 +* 如果两个key有相同的hash值,他们会被放在table数组的同一个桶里面。 +* key的equals()方法用来确保key的唯一性。 +* value对象的equals()和hashcode()方法根本一点用也没有。 diff --git a/source/_posts/linux/2.1、shell编程(2)-从入门到重新入门.md b/source/_posts/linux/2.1、shell编程(2)-从入门到重新入门.md index d499140..b9ae9d5 100644 --- a/source/_posts/linux/2.1、shell编程(2)-从入门到重新入门.md +++ b/source/_posts/linux/2.1、shell编程(2)-从入门到重新入门.md @@ -113,10 +113,10 @@ fi |运算符|含义|其他表示方式|备注| |-----|---|---------| -|-eq|是否相等|==| -|-ne|是否不相等|!=| -|-gt|大于|>| -|-lt|小于|<| +|-eq|是否相等|==|| +|-ne|是否不相等|!=|| +|-gt|大于|>|| +|-lt|小于|<|| |-ge|大于等于|\>=|使用\>=需要使用(( ))| |-le|小于等于|<=|使用<=需要使用(( ))| ```bash @@ -133,11 +133,13 @@ fi > 关系运算符只能用于整数 , 或者能够解析为整数的字符串 #### 布尔运算 + |运算符|含义|其他表示方式| |-----|----|---------| |!|非|| -|-o|或|\|\|| +|-o|或|||| |-a|与|&&| + ```bash # a小于10 并且 b大于20 if [ $a -lt 10 -a $b -gt 20 ] @@ -192,6 +194,7 @@ fi ### 文件测试运算符 一个字符串也可以表示一个文件(目录)的路径 使用这些方式可以获得这个文件的各种信息 + |运算符|含义| |------|----| |-b file |检测文件是否是块设备文件,如果是,则返回 true| diff --git a/source/images/Java/HashMap1.png b/source/images/Java/HashMap1.png new file mode 100644 index 0000000..5feebaa Binary files /dev/null and b/source/images/Java/HashMap1.png differ diff --git a/source/images/Java/HashMap2.png b/source/images/Java/HashMap2.png new file mode 100644 index 0000000..e10a7fd Binary files /dev/null and b/source/images/Java/HashMap2.png differ