第9章 Java的Util包

目标一 Collection/接口

为了满足特定的开发需求,选择合适的Collection/接口。

本节需要注意的问题

虽然没有特别的提到,但是对本小节的集合类的知识点是Java2版本考试的新考点之一,考试中关于新出现的集合类的题目非常简单,只需要应试者知道在哪如何使用这些类,而不需要应试者完全清楚底层细节的方法和字段。

旧的Collection /接口

Java2通过一些新增加的类/接口加强了集合类的用途,早一些的Java版本的Collection类包括:

vector

hashtable

array

BitSet

在这些类当中,只有array包含在1.1版本的认证考试的考点,从Java1.1开始,就是对所有开发情况中经常需要用到的排序功能提供了支持是导致Java越来越臃肿的一个原因。

新的Collection /接口

集合类的最底层是Collection接口,它提供了一系列所有集合类开发中常用到的方法。在开发中,或许你从未在你创建的类中实现Collection接口,那是因为Java提供了一系列Collection接口的子类/接口。

Java2API包含了以下几个新的Collection接口

Sets

Maps

所有实现Collection 接口的类存储对象为元素而不是原始数据类型,这种机制有个缺点就是创建对象对性能的影响,而且元素在使用之前必须从Object类型强制转换成合适的类型,这也同时意味着集合类不要求元素是同一类型的,因为一个Object对象可以是任何东西。

Set

Set是一个不可包含重复元素的集合类接口,这恰好和关系数据库中返回某一条记录的set 概念相符合。Set接口的奥妙就在于它的add方法。

add(Object o)

任何一个传递给add方法的对象必须实现equals方法,这样保证与已存数据进行对比。如果已经存在该数据,那么调用add方法不会对该set起任何影响并且返回false。这种试图添加一个元素返回false的思想更像是C/C++中使用的机制而不是Java,在这种情况下,大多数Java的其他类似添加方法选择的是抛出异常。

List

list是一个有序的可以有重复元素的集合类接口,该接口中一些重要的方法如下:

add

remove

clear

JDK的帮助文档给出了使用List处理一个实际GUI list进行控制一列包含名为Planets的列表的例子。

Map

Map是一个接口,实现它的类不能包含重复的key,这一点和hashtable很相似。

为什么我们使用集合类而不使用数组?

相比较而言,集合类相对于数组一个最大的优点就是它可以自增长,你不需要在创建它的时候为它分配大小空间,缺点就是集合类只能存储Object对象,而不能存储原始数据类型,因此不可避免的影响了一定的性能。数组不能直接支持排序,但是这点可以通过使用静态的集合类方法来克服。以下是一个例子。

import java.util.*;

public class Sort{

public static void main(String argv[]){

Sort s = new Sort();

}

Sort(){

String s[] = new String[4];

s[0]="z";

s[1]="b";

s[2]="c";

s[3]="a";

Arrays.sort(s);

for(int i=0;i< s.length;i++)

System.out.println(s[i]);

}

}

使用 Vector

下面的例子解释了怎样将不同类的对象添加到一个Vector里面.这与数组不同,不要求每个元素必须同类型.代码会将每个对象输出到标准输出设备,它隐性调用了每个对象的toString()方法到了Java2 Vector类成为创建一个可变大小数据结构的主要方法.可以用remove()方法从Vector类移出元素

import java.awt.*;

import java.util.*;

public class Vec{

public static void main(String argv[]){

Vec v = new Vec();

v.amethod();

}//End of main

public void amethod(){

Vector mv = new Vector();

//Note how a vector can store objects

//of different types

mv.addElement("Hello");

mv.addElement(Color.red);

mv.addElement(new Integer(99));

//This would cause an error

//As a vector will not store primitives

//mv.addElement(99)

//Walk through each element of the vector

for(int i=0; i< mv.size(); i++){

System.out.println(mv.elementAt(i));

}

}//End of amethod

}

Java2之前Vector类是创建可重新分配大小的数据结构的主要手段。可以使用remove()方法将元素从Vector中剔除掉。

使用 Hashtables

Hashtables有点像 Visual Basic中使用键来索引相应的键值的概念。除了用数值来对应元素以外,它的效果很像Vector。哈希表的名字部分通过引用数学概念中的数字索引概念来进行解决。一个hashtable Vector 优越的地方就在于快速的查找。

BitSet

正如BitSet所暗示的,它存储的是一个序列比特。不要被它名字部分的set部分误导,它不同于数学中或者数据库领域的set,并且它和Java2中提供Sets 没有任何关联。它更适合于被看作一个存储比特的容器。一个BitSet适合更有效率的存储一序列代表正/否值的比特值。其他可供选择的某些集合类在存储布尔值方面不及它有效率。

参考一下Bruce Eckel的《Thinking in Java》:

如果仅仅是从存储的角度来看它是很有效率;如果你期待更有效率的访问,它比一些原生类型的数组要稍微慢一些。

BitSet是一个在开发中从未需要使用的比较生僻的类,我认为它在密码领域或者图片处理的开发过重使用起来比较方便。下面让我看看你是否能够应付来自Java2考试的习题。

练习题

习题1

下面哪些是集合类? 1) Collection 2) Iterator 3) HashSet 4) Vector

习题2

关于Collection interface下面哪些是正确的? 1) The Vector class has been modified to implement Collection 2) The Collection interface offers individual methods and Bulk methods such as addAll 3) The Collection interface is backwardly compatible and all methods are available within the JDK 1.1 classes 4) The collection classes make it unnecessary to use arrays

习题3

下面哪些是正确的? 1) The Set interface is designed to ensure that implementing classes have unique members 2) Classes that implement the List interface may not contain duplicate elements 3) The Set interface is designed for storing records returned from a database query 4) The Map Interface is not part of the Collection Framework

习题4

下面哪些是正确的? 1) The elements of a Collection class can be ordered by using the sort method of the Collection interface 2) You can create an ordered Collection by instantiating a class that implements the List interface 3) The Collection interface sort method takes parameters of A or D to change the sort order, Ascending/Descending 4) The elements of a Collection class can be ordered by using the order method of the Collection interface

习题5

你希望存储少量数据并能快速访问. 你并不需要排序这些数据, uniqueness is not an issue and the data will remain fairly static 那种数据结构最适合这种需求? 1) TreeSet 2) HashMap 3) LinkedList 4) an array

习题6

下面哪些是Collection? 1) ListBag 2) HashMap 3) Vector 4) SetList

习题7

怎样从Vector中移出元素? 1) delete method 2) cancel method 3) clear method 4) remove method

练习题答案

答案1

3) HashSet 4) Vector 另外两个是接口不是类

答案2

1) Vector类已经被修改用类实现Collection 2) 集合类方法提供了单个的方法和addAll等的批量方法。 集合类是随着JDK1.2新推出的. 除了旧的集合类如Vetor,Bitset, 如果你在旧的平台上运行包含了新集合类的代码,将会抛出异常。

答案3

1) Set接口是为了确保正在执行的类有特定的成员。 实现List接口的对象中可以包含重复的元素.尽管一个实现Set接口的类的元素存储的可能是用来数据库查询结果, 但它不是为了那个目的专门设计的。

答案4

2) 你可以通过实力化一个实现List接口来创建一个有序的集合类

答案5

4) 一个数组 像这些简单的需求用数组是最合适的了。

答案6

2) HashMap 3) Vector JDK1.2Java2)中Vector这个类被“加装到”集合框架中来了。

习题7

4) remove方法

目标二 实现hashCode

正确与错误hashCode实现方法的区别

本节需要注意的问题

此宗旨是与JDK1.4的发布新推出的. Sun的网站上显示这个宗旨时,用小写字母c拼写hashcode,但从Object对象继承的方法却用了大写的C拼作hashCode.这是一个已经引入到此宗旨的奇怪的话题,处理大量的非常严肃的java编程,却不必麻烦你实现hashcode.真正的数据库例子不会让你困惑于此问题,但是这是你应该理解的宗旨.

它来自Object对象

这个hashcode方法继承自所有类对象的父类,所以任何对象的实例都可以调用hashcode方法,hashcode方法的签名是:

public int hashCode()

所以,你可能会遇到hashcode的一些伪签名,比如硕返回非int值或者带有非空参数.尽管如此,我怀疑问题会比这个稍微理论化一些.

返回int值是基于hash的集合类的特殊应用,例如

HashTable, HashSet, HashSet

基于hash的集合的本质是存储键、值.你用键来查找值。所以,举个例子,你可以用一个HashMap来存储职工的id作为键,职工名字作为值。

通常,一个hashcode值会是对象的内存地址。你可以很容易地用一些琐碎的代码来示范这个:

public class ShowHash{

public static void main(String argv[]){

ShowHash sh = new ShowHash();

System.out.println(sh.hashCode());

}

}

当我编译、运行这段代码,就会输出7474923,这个就是运行程序时这个类内存地址的表示。这就说明了一个hashcode的一个特性:在运行不同程序时它会得到不同的值。如果你考虑一个对象的内存地址,就不能确定一个程序的不同运行所得到的值。

这里有一段来自JDK1.4的引用,它包含了一个hashcode值的要求

“不管什么时候,当一个java应用程序执行期间,多次援引同一个对象,hashCode方法必须一致地返回同样的integer类型,对象上的无信息应用的equals比较就被修改了。这个整数在一个应用程序的不同执行可以不必保持一致性。”

既然它说,在同样的程序运行中hashCode的返回值必须一致,改变了对象上的无信息应用的equals比较,这个就告诉我们equalshashCode方法之间的关系了。

equals hashCode

由于每个对象都继承自一个叫Object的最终父对象,所以它们都可以访问equals方法。但是,当默认情况下,它只是简单地比较对象的内存地址。在用String类的时候,它的弊端就戏剧性地暴露出来了。如果String类不实现equals方法自己的版本,在比较两个字符串的时候就会比较它们的内存地址,而不是字符串序列。这显然不是你想要的,基于此,String类实现了自己的equals方法,可以比较两个字符串。

这里有API文档的另一个重点。

如果两个对象用对象的equals方法比较是相等的,那么它们调用hashCode方法必须生成同样的整数值。

此原则可以用下面的代码来解释

public class CompStrings{

public static void main(String argv[]){

String s1 = new String("Hello");

String s2 = new String("Hello");

System.out.println(s1.hashCode());

System.out.println(s2.hashCode());

Integer i1 = new Integer(10);

Integer i2 = new Integer(10);

System.out.println(i1.hashCode());

System.out.println(i2.hashCode());

}

}

每次程序的运行,这段代码都可以输出s1s2i1i2的同样hashCode值。理论上,在不同情况下会输出不同值。

当两个对象不等时

就象上面所写,用equals方法判断两个不同的对象一定会返回不同hashCode值,这是一个看似合理的推断。实际上不是,就像API文档所说的那样。

如果两个依据java.lang.Objectequals方法不相等的两个对象,分别调用hashCode方法一定会生成不同的整数值,这显然是不确定的。但是程序员应该了解这些。

同时,你也可以查询原始的API文档来理解hashCode方法的要求。

练习题

习题1 )下面所述哪些是正确的?

1)一个对象的hashCode方法会返回任何原始的整数类型

2)依据equals方法,两个相等的对象调用hashCode方法会生成同样的结果。

3)一个对象的hashcode方法在一个应用程序的不同执行,一定会返回同样的值。

4) Object类的hashcode方法签名是public int hashCode()

习题 2)

定义:

public class ValuePair implements Comparable{

private int iLookUp;

 

 

public ValuePair(int iLookUp, String sValue){

this.iLookUp=iLookUp;

}

 

 

public void setLookUp(int iLookUp){

this.iLookUp = iLookUp;

}

public int getLookUp(){

return iLookUp;

}

 

public boolean equals(Object o){

if( o instanceof ValuePair){

ValuePair vp = (ValuePair) o;

if(iLookUp == vp.getLookup()){

return true;

}

return false;

}

 

 

public int compareTo(Object o) {

ValuePair vp = (ValuePair) o;

Integer iwLookUp= new Integer(vp.getLookUp());

if(iwLookUp.intValue() < iLookUp){

return -1;

}

 

if(iwLookUp.intValue() > iLookUp){

return +1;

}

return 0;

}

 

}

下面那个是有效的hashCode方法

1)

public int hashCode() {

return (int) System.currentTimeMillis();

}

2)

public char hashCode(){

reutrn (char) iLookUp;

}

3)

public int hashCode(){

return iLookUp;

}

4)

public int hashCode(){

return iLookup * 100;

}

习题 3)

给出下面的代码

public class Boxes{

String sValue;

Boxes(String sValue){

this.sValue=sValue;

}

 

public String getValue(){

return sValue;

}

public boolean equals(Object o){

String s = (String) o;

if (sValue.equals(s) ){

return true;

}else{

return false;

}

}

public int hashCode(){

return sValue.hashCode();

}

}

哪些是正确的

1) 正确地执行hashCode方法 2)此类不会编译,因为String没有hashCode方法 3)不正确地执行hashCode方法 4)类不会编译,因为compareTo方法不会执行

习题 4

判断对错

如果正确地创建了一个对象,那么调用它的hashCode方法将返回同样的值

1) True 2) False

练习题答案

答案 1)

2)依据equals方法,两个相等的对象调用hashCode方法会生成同样的结果。

4) Object类的hashcode方法签名是public int hashCode()

答案 2)

3)

public int hashCode(){

return iLookUp;

}

4)

public int hashCode(){

return iLookup * 100;

}

hashCode方法必须返回整数值就排除了返回一个char值的选项2,选项1返回了毫秒形式的time类型,由于程序的单次运行一定会得到不同的值,所以就破坏了hashCode的一个特殊要求。正确选项34可能不是hashCode方法的好版本,但是它们一致地得到相等值并返回正确的数据类型

答案 3)

1)正确地执行hashCode方法

String类有hashCode方法的自己实现。如果没有,它会继承Object对象的hashCode方法,此方法简单地返回对象实例的内存地址。

答案 4

2) False

小心任何带always词的问题。对象类的hashCode方法默认返回对象的内存地址。Java工作的一些知识告诉我们,不同执行不一定会得到同样的内存地址。一个hashCode方法在一个程序的同样运行下一定会返回同样的值,但在不同运行下不一定会。如果你测试一个对象实例的hashCode,你可能发现在多程序运行期间,好像返回同样的内存地址,但是这并不是确定的。

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

光义

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值