《Thinking in Java》学习——17章容器深入研究(二)

理解Map

1.映射表的基本思想是它维护的是键-值(对)关联,因此你可以使用键来查找值。
2.下面是一个简单的关联数组的创建:

public class AssociativeArray<K, V> {
    private Object[][] pairs;
    private int index;
    public AssociativeArray(int length) {
        pairs = new Object[length][2];
    }
    public void put(K key, V value) {
        if (index >= pairs.length) 
            throws new ArrayIndexOutOfBoundsException();
        pairs[index++] = new Object[]{key, value};
    }
    @SupressWarning("unchecked")
    public V get(K key) {
        for (int i = 0; i < pairs.length; i++) {
            if (key.equals(pairs[i][0])) {
                return (V) pairs[i][1];
            }
        }
        return null;
    }
}

上面的代码只是具有一定的说明性,但是缺乏效率,并且由于具有固定尺寸而显得很不灵活。

一.性能

1.HashMap使用了特殊的值,称为散列码,来取代对键的缓慢搜索。散列码是相对唯一的、用以代表对象的int值,他是通过将该对象的某些信息进行转换而生成的。
2.hashCode()是类Object中的方法,因此所有Java对象都能产生散列码。HashMap就是使用对象的hashCode()进行快速查询的,此方法能够显著提高性能。
3.下面是几种基本的Map的实现:

Method 实现描述
HashMap Map基于散列表带你实现(它取代了Hashtable)。插入和查询“键值对”的开销是固定的。可以通过构造器设置容量和负载因子,以调整容器的性能
LinkedHashMap 类似于HashMap,但是便利它时,取得“键值对”的顺序是其插入顺序,或是最近最少使用的顺序。只比HashMap慢一点;而在迭代访问时反而更快,因为它使用链表维护内部的次序
TreeMap 基于红黑树的实现。查看“键”或“键值对”时,它们会被排序(由ComparableComparator决定)。TreeMap的特定在于,所得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map。它可以返回一个子树
WeekHashMap 弱键映射,允许释放映射所指向的对象;这是为解决某类特殊问题而设计的。如果映射之外没有引用指向某个“键”,则此“键”可以被垃圾回收器回收
ConcurrentHashMap 一种线程安全的Map,它不涉及同步加锁
IdentityHashMap 使用==代替equals()对“键”进行比较的散列映射。

4.对Map中使用的键的要求与对Set中的元素的要求一样。任何键都必须具有一个equals()方法;如果键被用于散列Map,那么它必须还具有恰当的hashCode()方法;如果键被用于TreeMap,那么它必须实现Comparable
5.关联数组中的基本方法是put()get()keySet()方法返回一个由Map的键组成的Set。可以直接通过values()方法获取“值”,该方法会产生一个包含Map中所有“值”的Collection,需要注意的是对该Collection的任何改动都会反映到与之相关联的Map

二.SortedMap

1.使用SortedMapTreeMap是其现阶段的唯一实现),可以确保“键”处于排序状态。
2.SortedMap接口中的一些特殊方法如下:

Method 功能描述
Comparator comparator() 返回当前Map使用的Comparator;或者返回null,表示以自然方式排序
T firstKey() 返回Map中的第一个键
T lastKey() 返回Map中的最末一个键
SortedMap subMap(fromKey, toKey) 生成此Map的子集,范围由fromKey(包含)到toKey(不包含)的键确定
SortedMap headMap(toKey) 生成此Map的子集,由小于toKey的所有键值对确定
SortedMap tailMap(fromKey) 生成此Map的子集,由大于或等于fromMap的所有键值对组成
三.LinkedHashMap

1.为了提高速度,LinkedHashMap散列化所有的元素,但是在遍历键值对时,却又以元素的插入顺序返回键值对。
2.可以在构造器中设定LinkedHashMap,使之采用基于访问的最近最少使用算法。于是没有被访问过的元素酒会出现在队列的最前面。对于需要定期清理元素以节省空间的程序来说,此功能使得程序很容易就能实现。

散列与散列码

1.使用标准类库中的类来作为HashMap的键没有问题,因为它具备了键所需的全部性质。
2.当自己创建用作HashMap的键的类,有可能会忘记在其中放置必需的方法,而这是通常会犯的一个错误。
3.可能你会认为,只需编写恰当的hashCode()方法的覆盖版本即可。但是它仍然无法正常运行,除非你同时覆盖equals()方法,它也是Object的一部分。HashMap使用equals()判断当前的键是否与表中的相同。
4.正确的equals()方法必须满足的条件:
(1)自反性。对任意xx.equals(x)一定返回true
(2)对称性。对任意xy,如果y.equals(x)返回true,则x.equals(y)一定返回true
(3)传递性。对任意xyz,如果有x.equals(y)返回truey.equals(z)返回true,则x.equals(z)一定返回true
(4)一致性。对任意xy,如果对象中用于等价比较的信息没有改变,那么无论调用x.equals(y)多少次,返回的结果应该保持一致,要么一直是true要么一直是false
(5)对任何不是nullxx.equals(null)一定返回false

一.理解hashCode()

1.使用散列的目的是:想要试用一个对象来查找另外一个对象。

二.为速度而散列

1.散列的价值在于速度:散列使得查询得以快速进行。
2.散列将键保存在某处,以便能够快速找到。存储一组元素最快的数据结构是数组吗所以使用它来表示键的信息
3.数组并不保存键本身。而是通过键对象生成一个数字,将其作为数组的下标。这个数字就是散列码,由hashCode()方法生成。
4.为解决数组容量被固定的问题,不同的键会产生相同的下标。也就是说,可能会有冲突。因此,数组多大就不重要了,任何键总能在数组中找到它的位置。
5.查询一个值的过程首先就是计算散列码,然后使用散列码查询数组。如果能够没有冲突,那就有了一个完美的散列函数,但这只是特例。通常。冲突由外部链接处理:数组并不直接保存值,而是保存值的list。然后对list中的值使用equals()方法进行线性查询。这部分的查询自然会比较慢,但是,如果散列函数好的话,数组的每个位置就只有较少的值。因此,不会查询整个list,而是快速地跳到数组的某个位置,只对很少的元素进行比较。

三.覆盖hashCode()

1.在使用HashMap的时候,你无法控制数组的下标值的产生。这个值依赖于具体的HashMap对象的容量,而容量大改变与容器的充满程度和负载因子有关。
2.设计hashCode()最重要的一个因素:无论何时,对同一个对象调用hashCode()都应该产生同样的值。
3.不应该使hashCode()依赖于具有唯一性的对象信息,尤其是使用this的值。因为这样做无法生成一个新的键,使之与put()中原始的键值对中的键相同。
4.hashCode()必须基于对象的内容生成散列码。散列码不鄙视独一无二的,但是通过hashCode()equals(),必须能够完全确定对象的身份。
5.好的hashCode()应该产生分布均匀的散列码。如果散列码都集中在一块,那么HashMap或者HashSet在某些区域的负载会很重,这样就不如分布均匀的散列函数快。
6.产生hashCode()的基本指导:
(1)给int变量result赋予某个非零值常量,例如17。
(2)为对象内每一个有意义的域f(即每个可以做equals()操作的域)计算出一个int散列码c

域类型 计算
boolean c = (f ? 0 : 1)
bytecharshortint c = (int)f
long c = (int) (f ^ (f >>> 32))
float c = Float.floatToBits(f)
double long l = Double.doubleToLongBits(f) c = (int) (l ^ l >>> 32)
Object,其equals()调用这个域的equals() c = f.hashCode()
数组 对每个元素应用上面的规则

(3)合并计算得到的散列码:
result = 37 * result + c;
(4)返回result
(5)检查hashCode()最后生成的结果,确保相同的对象有相同的散列码。
7.compareTo()的创建方式:排序的规则首先按照实际类型排序,然后如果有名字的话,按照name排序,最后安好创建的顺序排序。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容

  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-java.h...
    eddy_wiki阅读 1,152评论 0 16
  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,254评论 0 16
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,582评论 18 399
  • Java8张图 11、字符串不变性 12、equals()方法、hashCode()方法的区别 13、...
    Miley_MOJIE阅读 3,693评论 0 11
  • 附记南迁得失 或问南迁得失何如?予应之曰:当自成逾秦入晋,势已破竹,惟南迁一策,或可稍延岁月,而光时亨以为邪说,其...
    陇右雷柯柯阅读 190评论 0 1