Java编程思想(十四)

2019-05-27  本文已影响0人  MikeShine

第17章 容器深入探究

之前我们在第11章里面介绍过容器,分两类,Collection&Map,其中 Collection包括 List/Set/Queue等。
这一章是在之前介绍容器的基础知识之上,从底层实现的角度来对容器进行讲解。

17.2 填充容器

之前在数组相关的小节(16.6),介绍了填充数组的方法,分别用 Arryas.fill()方法 和 生成器的方法来进行填充。
这里对容器也是一样的,填充容器,也下面几种方法。

public class FillingLists {
    public static void main(String argsp[]){
        List<Integer> list = new ArrayList<Integer>(Collections.nCopies(4,0));
        System.out.println(list);
        Collections.fill(list,1);
        System.out.println(list);
    }
}

除了上面两种之外,在11.3小节我们还给出了 Arrays.asList()方法 & Collections.addAll() 方法。

17.3 Collection 的功能方法

这里给出了 Collection类中一些常用的方法。

17.4 可选操作

这里主要说了一下 UnsupportedOperationException这个异常,给了一个例子,当我们 用 Arrays.asList() 方法将一个数组转成 List之后,我们对这个引用再做一些改变其长度的操作,由于底层还是一个数组,所以就会报这样的错。这叫做“使用了不正确的接口实现”。
这里正确的做法,跟我们之前说过的一样,将 Arrays.asList()方法结果作为构造器,这样就会生成新的可以改变大小的底层对象了。

17.5 List的功能方法

这里作者给出了一个例子,说明了一下 List中不常用的一些方法。

17.6 Set 和存储顺序

首先来说,存入Set的每个元素都需要保证唯一性,这是通过 equals() 方法来保证的。
这里 Set 包含了:

这里在书上给出了关于上述几个点的总结。


总结

这一个小节说的是什么意思呢?在书上作者给出了一段代码,说我们如果定义 一个 SetType,在定义相关的 HashType/ TresType 等这种基于第一种的新的数据结构时,需要重写的方法。
实际上是从底层代码向我们解释了一下,Set/HashSet/TreeSet/LinkedHashSet之间的 关系。

17.7 队列

这一个小节都是说一些底层的东西。

17.8 理解Map

这里书上给出了一个例子,说明了Map这种关联数组是如何实现的。

17.8.1 性能

HashMap 使用了特殊的值,称作 散列码 Hash Code,来取代对key的缓慢搜索。散列码是“相对唯一” 的、用以代表对象的 int 值,他是通过将该对象的某些信息进行转换而生成的。
hashCode() 是Object类中的方法,因此所有的对象都可以生成散列码。
对 Map 中使用的键的要求与对 Set 中的元素的要求是一样的:

17.9 散列与散列码

HashMap使用 equals() 判断当前的键是否与表中存在的键相同。
如果要使用自己的类作为 HashMap 的键,必须同时重写 hashCode() & equals().

17.9.1 散列概念

使用 hashCode() 的目的在于:想用一个对象来查找另外一个对象。
而Map的实现类使用散列是为了提高查询速度。

散列的价值在于速度:散列使得查询得以快速进行。由于瓶颈位于查询速度,因此解决方案之一就是保持键的排序状态,然后使用Collections.binarySearch()进行查询。
散列则更进一步,它将键保存在某处,以便能够很快找到。存储一组元素最快的数据结构是数组,所以用它来表示键的信息(请小心留意,我是说键的信息,而不是键本身)。但是因为数组不能调整容量,因此就有一个问题:我们希望在Map中保存数量不确定的值,但是如果键的数量被数组的容量限制了,该怎么办?
答案就是:数组并不保存键本身。而是通过键对象生成一个数字,将其作为数组的下标。这个数字就是散列码,由定义在Object中的、且可能由你的类覆盖的hashCode()方法(在计算机科学的术语中称为散列函数)生成。
为解决数组容量固定的问题,不同的键可以产生相同的下标。也就是说,可能会有冲突,即散列码不必是独一无二的。因此,数组多大就不重要了,任何键总能在数组中找到它的位置。

17.9.2 理解散列

综上来说,散列就是把一个对象(key)生成一个数字保存下来(作为数组的下标),然后在查找这个对象时,直接找到这个数字就可以了,所以散列就是为了提高查找速度。
手段就是将一个对象生成的数字与其关联并保存下来(通过数组,成为散列表),而这个生成的数字就是 散列码。生成这个散列码的方法就是散列函数 hashCode().
因此,HashMap中查询一个key的过程就是:

因此,不是查询整个list,而是快速地跳到数组的某个位置,只对很少的元素进行比较。这便是HashMap会如此快速的原因。

17.9.3 覆盖 hashCode()

这里书上说了如果编写自己的Map类的实现类,需要覆盖 hashCode() 方法和 equals() 方法。
作者给出了如何编写自己的 hashCode() 散列函数。
这里先不看了。

上一篇 下一篇

猜你喜欢

热点阅读