词典的实现(2)-借助顺序表(数组)实现词典

一,思路

1,词典实现类ArrayDictionary.java肯定要implements 词典的接口DictionaryInterface.java(参考:http://www.cnblogs.com/hapjin/p/4573826.html)。其次,为了序列化,还要implements Serializable接口

2,由于词典中的每个元素是一个键值对。因此,需要将查找键 封装在同一个对象中,因而定义了Entry.java来表示词典中的每个元素。其中,Entry类是作为ArrayDictionary的内部类来实现的。

3,由于每个Entry对象表示一个词典元素,因此ArrayDictionary.java需要一个Entry类型的数组来存放词典元素。这也是文章的标题--借助顺序表来实现词典的原因。

再深入讨论下,我们知道在JAVA类库中ArrayList.java就相当于已经帮程序员实现好了一个顺序表(数组),我们不需要再去关注数组的具体实现细节以及如何遍历数组,而是直接用ArrayList.java中的各种方法(add,remove……)来完成数组的基本操作,通过 iterator()方法获得遍历数组的迭代器。也可以借助ArrayList.java中方法来辅助实现DictionaryInterface接口中定义的方法。

但是,在本例中,采用的是自定义数组来实现词典的,因此就需要根据DictionaryInterface接口中的方法以及自已的需要来完成ArrayDictionary.java中的方法。

下面,具体理清下迭代器的实现思路:在ArrayDictionary.java中定义了三个私有内部类分为表示实现三个迭代器:

class EntryIterator<Entry>实现遍历Entry数组的迭代器

class KeyIterator<K>实现遍历查找键的迭代器

class ValueIterator<V>实现 值(Value) 的迭代器

其中,后两个迭代器会用到遍历Entry数组的迭代器,因为键(Key)和值(Value)是封装在Entry中的。

同时,还ArrayDictionary.java定义了三个相应的获取遍历器的方法来获取上述三个遍历器。

 

ArrayDictionary.java代码如下:

  1 package dictionary;
  2 
  3 import java.io.Serializable;
  4 import java.util.Iterator;
  5 import java.util.NoSuchElementException;
  6 
  7 public class ArrayDictionary<K,V> implements DictionaryInterface<K, V>, Serializable{
  8 
  9     private Entry<K,V>[] dictionary;//封装了 key-value对  的对象
 10     private int currentSize;//数组元素个数
 11     private final static int DEFAULT_SIZE = 25;
 12     
 13     public ArrayDictionary(){
 14         this(DEFAULT_SIZE);
 15     }
 16     @SuppressWarnings("unchecked")
 17     public ArrayDictionary(int initialCapacity){
 18         dictionary = new Entry[initialCapacity];
 19         currentSize = 0;
 20     }
 21     
 22     private class Entry<S,T>{
 23         S key;
 24         T value;
 25         
 26         private Entry(S searchKey, T dataValue){
 27             key = searchKey;
 28             value = dataValue;
 29         }
 30         //键是不可改变的,因此没有setKey方法
 31         public S getKey() {
 32             return key;
 33         }
 34         public T getValue() {
 35             return value;
 36         }
 37         public void setValue(T value) {
 38             this.value = value;
 39         }
 40     }//end class Entry
 41     
 42     /*
 43      * EntryIterator类用来实现Entry数组的遍历器
 44      */
 45     private class EntryIterator<Entry> implements Iterator<Entry>, Serializable{
 46         private int nextIndex;//标记当前正在遍历的元素
 47         
 48         private EntryIterator(){
 49             nextIndex = 0;
 50         }
 51         @Override
 52         public boolean hasNext() {
 53             return nextIndex < currentSize;
 54         }
 55         @Override
 56         public Entry next() {
 57             if(hasNext()){
 58                 Entry result = (Entry) dictionary[nextIndex];//将泛型的Entry转换为非泛型的Entry???
 59                 nextIndex++;
 60                 return result;
 61             }
 62             else
 63                 throw new NoSuchElementException();
 64         }
 65         @Override
 66         public void remove() {
 67             // TODO Auto-generated method stub
 68             throw new UnsupportedOperationException();
 69         }
 70     }
 71     
 72     private class KeyIterator<K> implements Iterator<K>{
 73         
 74         private Iterator<Entry> entryIterator;
 75         private KeyIterator(){
 76             entryIterator = new EntryIterator<ArrayDictionary.Entry>();
 77         }
 78         @Override
 79         public boolean hasNext() {
 80             return entryIterator.hasNext();//有Entry元素就表示有Key(同时还会有Value)
 81         }
 82 
 83         @Override
 84         public K next() {
 85             Entry nextEntry = entryIterator.next();
 86             return (K) nextEntry.getKey();//??泛型与非泛型之间的转换???
 87         }
 88 
 89         @Override
 90         public void remove() {
 91             throw new UnsupportedOperationException();
 92         }
 93         
 94     }
 95     
 96     private class ValueIterator<V> implements Iterator<V>,Serializable{
 97 
 98         private EntryIterator<Entry> entryIterator;
 99         private ValueIterator(){
100             entryIterator = new EntryIterator<ArrayDictionary.Entry>();
101         }
102         @Override
103         public boolean hasNext() {
104             return entryIterator.hasNext();
105         }
106 
107         @Override
108         public V next() {
109             Entry<K, V> nextEntry = entryIterator.next();
110             return nextEntry.getValue();
111         }
112 
113         @Override
114         public void remove() {
115             throw new UnsupportedOperationException();
116             
117         }
118         
119     }
120     
121     /*
122      * Task 根据查找键来 查找 该元素在Entry数组中的位置
123      * @param key 待查找的键
124      * @return 若查找成功,返回的index < currentSize .否则,index > currentSize
125      */
126     private int locateIndex(K key){
127         int index = 0;
128 //        System.out.println(key.getClass());
129         while((index < currentSize) && (!(key.equals(dictionary[index].getKey())))){
130 //            System.out.println("index = " + index);
131             index++;
132         }
133         return index;
134     }
135     
136     @Override
137     public V add(K key, V value) {
138         V result = null;
139         int KeyIndex = locateIndex(key);
140         if(KeyIndex < currentSize){
141             result = dictionary[KeyIndex].getValue();
142             dictionary[KeyIndex].setValue(value);
143         }
144         else{
145             if(isFull())
146                 doubleArray();
147             dictionary[currentSize] = new Entry<K, V>(key, value);
148             currentSize ++;
149             System.out.println("currentSize = " + currentSize);
150         }
151         return result;
152     }
153     
154     private void doubleArray(){
155         
156     }
157 
158     @Override
159     public V remove(K key) {
160         V result = null;
161         int keyIndex = locateIndex(key);
162         if(keyIndex < currentSize){
163             result = dictionary[keyIndex].getValue();//获得待删除key的value
164             //用最后一个元素代替待删元素,然后再删除最后一个元素
165             dictionary[keyIndex] = dictionary[currentSize - 1];
166             currentSize--;
167         }//end if
168         return result;
169     }
170 
171     @Override
172     public V getValue(K key) {
173         V result = null;
174         int keyIndex = locateIndex(key);
175         //System.out.println("searching key= " + keyIndex);
176         if(keyIndex < currentSize)//找到了
177             result = dictionary[keyIndex].getValue();
178         return result;
179     }
180 
181     @Override
182     public boolean contains(K key) {
183         return locateIndex(key) < currentSize; 
184     }
185 
186     @Override
187     public Iterator<K> getKeyIterator() {
188         // TODO Auto-generated method stub
189         return new KeyIterator();
190     }
191 
192     @Override
193     public Iterator<V> getValueIterator() {
194         // TODO Auto-generated method stub
195         return new ValueIterator();
196     }
197 
198     @Override
199     public boolean isEmpty() {
200         return currentSize == 0;
201     }
202 
203     @Override
204     public boolean isFull() {
205         System.out.println("dictionary.length = " + dictionary.length);
206         return currentSize == DEFAULT_SIZE;
207     }
208 
209     @Override
210     public int getSize() {
211         return currentSize;
212     }
213 
214     @Override
215     public void clear() {
216         currentSize = 0;
217     }
218 
219 }

 

词典数据结构实现完成之后,编写了一个TelephoneDirectory.java使用该词典来模拟电话本功能。以姓名为键,以电话号码为值来存储每条记录,这是ArrayDictionary实现的功能;从一个特定格式的文本文件中读取姓名及电话号码值,并将之存储到词典中,这是TelephoneDirectory的实现功能。

然后再编写客户端类Driver.java 来测试电话本的功能。测试流程如下:提供内容为  姓名  电话号码 的一个文本文件,在控制台上输入姓名,按回车查询该人名所对应的电话号码。

 

TelephoneDirectory.java代码如下:

 1 package dictionary.common;
 2 
 3 import java.util.Scanner;
 4 import dictionary.ArrayDictionary;
 5 import dictionary.DictionaryInterface;
 6 import dictionary.common.Name;
 7 
 8 public class TelephoneDirectory {
 9     private DictionaryInterface<Name,String> phoneBook;
10     
11     public TelephoneDirectory(){
12         phoneBook = new ArrayDictionary<Name,String>();
13     }
14     
15     /*
16      * @Task 以行为单位,依次读取整个文件
17      * @param data 读取文本文件中指定格式的数据:
18      * 每行含有三个字符串:名,姓,电话号码。它们用空格分开
19      */
20     public void readFile(Scanner data){
21         while(data.hasNext()){
22             String firstName = data.next();
23             String lastName = data.next();
24             String phoneNumber = data.next();
25             
26             Name fullName = new Name(firstName, lastName);
27             phoneBook.add(fullName, phoneNumber);
28         }//end while
29         data.close();
30     }//end readFile()
31     
32     public String getPhoneNumber(Name personName){
33         return phoneBook.getValue(personName);
34     }
35 }

 

电话号码查询客户端Driver.java实现代码如下:

 1 package dictionary.client;
 2 
 3 import java.io.File;
 4 import java.io.FileNotFoundException;
 5 import java.io.IOException;
 6 import java.util.Scanner;
 7 import dictionary.common.Name;
 8 import dictionary.common.TelephoneDirectory;
 9 
10 public class Driver {
11     private static final Name INPUT_ERROR = new Name("error", "error");
12     private static final Name QUIT = new Name("quit", "quit");
13 
14     public static void main(String[] args) {
15         TelephoneDirectory directory = new TelephoneDirectory();
16         String fileName = "data.txt";//file name can be read data into telephone book 
17         Scanner data = null;
18         try{
19             data = new Scanner(new File(fileName));
20         }catch(FileNotFoundException e){
21             System.out.println("File not found: " + e.getMessage());
22         }catch(IOException e){
23             System.out.println("I/O exception: " + e.getMessage());
24         }
25         directory.readFile(data);
26         
27         Name nextName = getName();//get name for search from user
28         while(! nextName.equals(QUIT)){
29             if(nextName.equals(INPUT_ERROR)){
30                 System.out.println("Error in entering name.Try again. ");
31                 break;
32             }
33             else{
34                 String phoneNumber = directory.getPhoneNumber(nextName);
35                 if(phoneNumber == null)
36                     System.out.println(nextName + " is not in the directory");
37                 else
38                     System.out.println("The phone number for " + nextName + " is " + phoneNumber);
39                 nextName = getName();
40             }
41         }//end while
42         System.out.println("Bye!");
43     }//end main
44 
45     // return either the name read from user, INPUT_ERROR ,or QUIT
46     private static Name getName() {
47         Name result = null;
48         Scanner keyboard = new Scanner(System.in);// 扫描从键盘的输入
49         System.out.println("Enter first name and last name,or quit to end");
50         String line = keyboard.nextLine();// 读取一行输入
51         if (line.trim().toLowerCase().equals("quit"))
52             result = QUIT;// 用户结束查询
53         else {// 构造 查询的 key对象-Name对象
54             String firstName = null;
55             String lastName = null;
56             Scanner scan = new Scanner(line);// 扫描输入的一行
57             
58             if (scan.hasNext()) {
59                 firstName = scan.next();// 提取出firstname
60                 //System.out.println("keyinput--firstName:" + firstName);
61                 if (scan.hasNext())
62                     lastName = scan.next();// 提取出lastname
63                 else
64                     result = INPUT_ERROR;// 用户没有输入lastname
65             } else
66                 result = INPUT_ERROR;
67             if (result == null)
68                 result = new Name(firstName, lastName);
69         }
70         return result;
71     }
72 }

 

 姓名是由firstName与lastName组成,Name.java是用来封装人名的类,Name类的对象作为Map的查找键。当两个人的firstName 与 lastName相同时,它们应该表示同一个人,因此需要重写Name类的hashCode() 和 equals().

重写的规则参考:http://www.cnblogs.com/hapjin/p/4582795.html

 1 package dictionary.common;
 2 
 3 public class Name {
 4     private String firstName;
 5     private String lastName;
 6     
 7     @Override
 8     public int hashCode() {
 9         final int prime = 31;
10         int result = 1;
11         result = prime * result
12                 + ((firstName == null) ? 0 : firstName.hashCode());
13         result = prime * result
14                 + ((lastName == null) ? 0 : lastName.hashCode());
15         return result;
16     }
17     @Override
18     public boolean equals(Object obj) {
19         if (this == obj)
20             return true;
21         if (obj == null)
22             return false;
23         if (getClass() != obj.getClass())
24             return false;
25         Name other = (Name) obj;
26         if (firstName == null) {
27             if (other.firstName != null)
28                 return false;
29         } else if (!firstName.equals(other.firstName))
30             return false;
31         if (lastName == null) {
32             if (other.lastName != null)
33                 return false;
34         } else if (!lastName.equals(other.lastName))
35             return false;
36         return true;
37     }
38     public Name(){
39         
40     }
41     public Name(String firstName, String lastName){
42         this.firstName = firstName;
43         this.lastName = lastName;
44     }
45     
46     public String getFirstName() {
47         return firstName;
48     }
49     public void setFirstName(String firstName) {
50         this.firstName = firstName;
51     }
52     public String getLastName() {
53         return lastName;
54     }
55     public void setLastName(String lastName) {
56         this.lastName = lastName;
57     }
58     
59     public String toString(){
60         return firstName + " " + lastName;
61     }
62 }

 

 

运行结果,文本文件中提供的数据如下:

xiaoming lee    2009999999
xiaohong lee    1009999999
xiaohei  lee    3009999999

查询结果如下:

 

转载于:https://www.cnblogs.com/hapjin/p/4582064.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值