Java数组与容器类分析资料

来源:java认证发布时间:2012-11-12 13:12:26java认证视频

  1.使用散列的目的:想要使用一个对象来查找另一个对象。使用 TreeSet 或 TreeMap 也能实现此目的。另外,还可以自己实现一个 Map ,此时,必须提供 Map.entrySet() 方法来生成 Map.Entry 对象的 Set 。

  2.使用散列的价值:速度,散列使得查询可以快速进行。散列将 label 保存载数组中方便快速查询,因为存储一组元素最快的数据结构是数组,用它来表示 label 的信息 ( 后面有信息的描述 ) ,而不是 label 本身。通过 label 对象计算得到一个数字,作为数组的下标,这个数字就是散列码 ( 即前面所述的信息 ) 。该散列码具体是通过定义在基类 Object 中,可能由程序员自定义的类覆盖的 hashCode() 方法,即散列函数生成。为了解决数组容量带来的限制,可以使不同的 label 生成相同的下标,保存在一个链表 list 中,每一个链表就是数组的一个元素。查询 label 时就可以通过对 list 中的信息进行查找,当散列函数比较好,数组的每个位置中的 list 长度较短,则可以快速查找到数组元素 list 中的某个位置,提高了整体速度。

  散列表中的 slot 通常称为 bucket ,为了使散列分步均匀, bucket 的值一般取质数。但事实证明,质数实际上并不是散列 bucket 的理想容量,近来 Java 散列实现都使用 2 的幂,具体如何验证以后再续。

  3.HashMap 的性能因子

  容量 (capacity): 散列表中 bucket 的数量。

  初始化容量 (initial capacity): 创建散列表时 bucket 的数量。可以在构造方法中指定 HashMap 和 HashSet 的初始化容量。

  尺寸 (size): 散列表中记录的数量。 ( 数组的元素个数,非 list 中元素总和 )

  负载因子 (load factor): 尺寸 / 容量。负载因子为 0 ,表示空的散列表, 0.5 表示半满的散列表。轻负载的散列表具有冲突少,适宜插入与查询的特点,但是使用迭代器遍历会比较慢。较高的负载会减少所需空间大小。当负载达到指定值时, 容器会自动成倍地增加容量,并将原有的对象重新分配,存入新的 bucket 中,这个过程称为“重散列”。

  4. 重写 hashCode() 的关键

  (1) 对同一个对象调用 hashCode() 都应该生成同样的值。

  (2) hashCode() 方法不要依赖于对象中易变的数据,当数据发生变化时, hashCode() 就会生成一个不同的散列码,即产生了一个不同的 label 。

  (3) hashCode() 不应依赖于具有唯一性的对象信息,例如对象地址。

  (4) 散列码应该更关心速度,而不是唯一性,因为散列码不必是唯一的。

  (5) 好的 hashCode() 应该产生分步均匀的散列码。在 Effective Java (Addison-Wesley 2001) 中, Joshua Bloch 给 hashCode() 给出了设计指导,可以参考。

  编写正确高效的 hashCode() 和 equals() 可以参考 Apache 的 Jakarta Commons 项目中的工具。

  java 集合类总结

  对象的集合

  如果程序的对象数量有限,且寿命可知,那么这个程序是相当简单的。

  数组

  数组与其它容器的区别体现在三个方面:效率,类型识别以及可以持有primitives。数组是Java 提供的,能随机存储和访问reference序列的诸多方法中的,最高效的一种。数组是一个简单的线性序列,所有它可以快速的访问其中的元素。但是速度是 有代价的;当你创建了一个数组之后,它的容量就固定了,而且在其生命周期里不能改变。也许你会提议先创建一个数组,等到快不够用的时候,再创建一个新的, 然后将旧的数组里的reference全部导到新的里面。其实(我们以后会讲的)ArrayList就是这么做的。但是这种灵活性所带来的开销,使得 ArrayList的效率比起数组有了明显下降。

  Java 对数组和容器都做边界检查;如果过了界,它旧会给一个RuntimeException。这种异常表明这个错误是由程序员造成的,这样你就用不着再在程序 里面检查了。

  还有一些泛型容器类包括List,Set 和Map。他们处理对象的时候就好像这些对象都没有自己的具体类型一样。也就是说,容器将它所含的元素都看成是(Java 中所有类的根类)Object的。这样你只需要建一种容器,就能把所有类型的对象全都放进去。从这个角度来看,这种做法很不错(只是苦了 primitive。如果是常量,你还可以用Java 的 primitive的Wrapper类;如果是变量,那就只能放在你自己的类里了)。与其他泛型容器相比,这里体现数组的第二革优势:创建数组的时候,你 也同时指明了它所持有的对象的类型(这又引出了第三点--数组可以持有primitives,而容器却不行)。也就是说,它会在编译的时候作类型检查,从 而防止你插入错误类型的对象,或者是在提取对象的时候把对象的类型给搞错了。Java 在编译和运行时都能阻止你将一个不恰当的消息传给对象。所有这并不是说使用容器就有什么危险,只是如果编译器能够帮你指定,那么程序运行会更快,最终用户 也会较少收到程序运行异常的骚扰。

  从效率和类型检查的角度来看,使用数组总是没错的。但是,如果你在解决一个更为一般的问题,那数组就会显得功能太弱了点。

视频学习

我考网版权与免责声明

① 凡本网注明稿件来源为"原创"的所有文字、图片和音视频稿件,版权均属本网所有。任何媒体、网站或个人转载、链接转贴或以其他方式复制发表时必须注明"稿件来源:我考网",违者本网将依法追究责任;

② 本网部分稿件来源于网络,任何单位或个人认为我考网发布的内容可能涉嫌侵犯其合法权益,应该及时向我考网书面反馈,并提供身份证明、权属证明及详细侵权情况证明,我考网在收到上述法律文件后,将会尽快移除被控侵权内容。

最近更新

社区交流

考试问答