HashMap的困难

时间:2015-04-15 21:17:50

标签: java maps hashtable

这是一项学校作业。总的来说,这应该读取一个文本文件,将其中的单词添加到哈希表中。我已将其编码,但我正在对其进行测试,并且它给了我一些问题。当我试图找到对象的索引时,它总是返回-1,这意味着它表示即使数组中的单词也不是。还有一些其他问题。这让我很头疼。

import java.util.Iterator;
import java.util.LinkedList;


public class MyMap<K,V> implements Iterable<MyMap.MyEntry<K,V>> {
        int collision; // maintain the current count of collisions here.
        int slots = 0;
        int key = 0;
        MyEntry<K,V> tempPair;
        LinkedList<MyEntry<K,V>> [] bucketArray;
        int keyMod;

    /**
     * Create a MyMap instance with the specified number of 
     * buckets.
     * 
     * @param buckets the number of buckets to make in this map
     */
    public MyMap(int buckets) {
        slots = buckets;
        bucketArray = (LinkedList<MyEntry<K,V>> [])new LinkedList[buckets];

    }

    /**
     * Puts an entry into the map.  If the key already exists, 
     *  it's value is updated with the new value and the previous
     *  value is returned.
     *
     * @param key the object used as a key to retrieve the value
     * @param value the object stored in association with the key
     * 
     * @return the previously stored value or null if the key is new
     */
    public V put(K key, V value) {
            // don't forget hashcodes can be any integer value.  You'll
            // need to compress them to ensure they give you a valid bucket.

        MyEntry<K,V> tempPair = new MyEntry<K,V>(key, value);
        Word newWord = new Word((String)key);
        keyMod = newWord.hashCode((String)key) % slots;


        if ((bucketArray[keyMod]) == null){
            LinkedList<MyEntry<K,V>> firstList = new LinkedList<MyEntry<K,V>>();
            firstList.add(tempPair);
            bucketArray[keyMod] = firstList;
            return null;
            }

        else {

            int indexNode = bucketArray[keyMod].indexOf(tempPair);

            if (indexNode == -1) {
                bucketArray[keyMod].add(tempPair);
                collision += 1;
                //System.out.println(indexNode );
                return null;
                }
            else {
                MyEntry<K,V> oldNode = bucketArray[keyMod].get(indexNode);
                V oldValue = oldNode.value;
                oldNode.value = tempPair.value;
                //System.out.println(indexNode );
                return oldValue;
            }
        }

    }

    /**
     * Retrieves the value associated with the specified key.  If 
     * it exists, the value stored with the key is returned, if no 
     * value has been associated with the key, null is returned.
     * 
     * @param key the key object whose value we wish to retrieve
     * @return the associated value, or null
     */
    public V get(K key) {
        //MyEntry<K,V> tempPair = new MyEntry<K,V>(key,value);


        Word newWord = new Word((String)key);
        int keyMod = newWord.hashCode((String)key) % slots;

        if (bucketArray[keyMod] == null) {
            return null;
        }

        else { 

            int temp = bucketArray[keyMod].indexOf(key);
            if (temp == -1) {
                return null;
                }

            else {
                MyEntry<K,V> tempNode = bucketArray[keyMod].get(temp);
                return tempNode.value;
                }
        }

    }

    /**
     *
     * I've implemented this method, however, you must correctly 
     * maintain the collisions member variable.
     *
     * @return the current count of collisions thus far.
     */
    public int currentCollisions(K key) {
        return collision;
    }
    /**
     * Looks through the entire bucket where the specified key
     * would be found and counts the number of keys in this bucket
     * that are not equal to the current key, yet still have the
     * same hash code.
     * 
     * @param key
     * @return a count of collisions
     */
    public int countCollisions(K key) {
        Word newKey = new Word((String) key);
        int keyMod = newKey.hashCode((String) key) % slots;
        if (bucketArray[keyMod].indexOf(key) == -1){
            return bucketArray[keyMod].size();
            }
        return (bucketArray[keyMod].size()-1);
    }
    /**
     * Removes the value associated with the specifed key, if it exists.
     * @param key the key used to find the value to remove.
     * @return the value if the key was found, or null otherwise.
     */
    public V remove(K key) {
        Word newWord = new Word((String)key);
        //int keyMod = newWord.hashCode((String)key) % slots;
        int tempNodeIndex = bucketArray[newWord.hashCode((String)key)].indexOf(key);
        if (tempNodeIndex == -1) {
            return null;
            }
        else{
        tempPair = bucketArray[key.hashCode()].get(tempNodeIndex);
        V returnValue = tempPair.value;
        tempPair.value = null;
        return returnValue;}
    }
    /**
     * Returns the number of entries in this map
     * @return the number of entries.
     */
    public int size() {
        int size = 0;
        for (int i =0; i< bucketArray.length; i++){
            size = bucketArray[i].size() + size;
        }
        return size;
    }

    /**
     * Creates and returns a new Iterator object that 
     * iterates over the keys currently in the map. The iterator 
     * should fail fast, and does not need to implement the remove
     * method.
     * 
     * @return a new Iterator object  
     */
    public Iterator<MyEntry<K,V>> iterator() {

        return null;
    }

    public static class MyEntry<K,V> {
        K key;
        V value;

        public MyEntry(K k, V v) {
            key = k;
            value = v;
        }
    }


}

这是Word Class

/* The reason you can't extend String Class is because String is a final class and you can not have 
 * a subclass that might alter components of a final class. Since the word class would extend the 
 * String class, it would have the capability to change variables within the String Final Class.
 */

public class Word {

    String word;

    /**
     * Creates a Word object representing the specified String
     * 
     * @param w the String version of this word.
     */
    public Word(String w) {
        word = w;

    }

    /**
     * Returns a hashcode for this Word -- an integer whose value is based on the 
     * word's instance data. Words that are .equals() *must* have the same hashcode.
     * However, the converse need not hold -- that is, it *is*  acceptable for 
     * two words that are not .equals() to have the same hashcode.
     */
    public int hashCode(String word) {
         int code = 0;
         for ( int i =0; i<word.length(); i++){
            code = word.charAt(i) + code;
         }

         return code;    //word.hashCode();

        //int hashCode = 0;
        //for ( int i = 0; i<word.length(); i++) {
            //hashCode = Math.abs(hashCode*13 + word.charAt(i));
        //}
        //System.out.println(hashCode);
        //return hashCode;
    }

    /**
     * Returns true if and only if this Word object represents the same
     * sequence of characters as the specified object. Here, you can assume
     * that the object being passed in will be a Word. 
     */
    public boolean equals(Object o) { 
        String passedIn = o.toString();
        boolean returnValue = word.equals(passedIn);

        return returnValue;
    }

    /**
     * This method returns the string representation of the object.
     * A correct implementation will return the String representation of the
     * word that is actually being stored. ie., if you had a word object representing
     * 'hi', it should return 'hi'
     */
    public String toString() {
        String thisString = word;
        return thisString;
    }
}

这是我的测试人员的开始:

import java.io.*;
import java.util.*;


public class Tester<K,V> {

    public static void main(String [] args) throws FileNotFoundException {

        MyMap<String, Integer> pain = new MyMap<String, Integer>(3000);

        Scanner s = new Scanner (new File("pg4.txt"));

        while (s.hasNext()) {
            String word = s.next();
            Integer value = (Integer) pain.get(word);

            if (value == null) {
                pain.put(word, 1);
            }
            else {
                value +=1;
                pain.put(word, value);
                }
            }
        s.close();
        pain.put("the",1);
        pain.put("the",5);
        pain.get("the");
        System.out.println("'the' gives this many collisions: " + pain.get("the") );
        pain.remove("the");
        System.out.println("'the' gives this many collisions: " + pain.get("the") );


    }
}

2 个答案:

答案 0 :(得分:1)

  1. indexOf使用equals进行比较,因此您对indexOf的调用无效。您需要为equals实施MyEntry

    public static class MyEntry<K,V> {
        K key;
        V value;
    
        public MyEntry(K k, V v) {
            key = k;
            value = v;
        }
    
        @Override
        public int hashCode() {
            // (overriding hashCode
            // just because we are overriding equals)
            return ( key == null ? 0 : key.hashCode() );
        }
    
        @Override
        public boolean equals(Object o) {
            if(!(o instanceof MyEntry<?, ?>))
                return false;
            MyEntry<?, ?> that = (MyEntry<?, ?>)o;
            return( this.key == null ?
                that.key == null : this.key.equals(that.key)
            );
        }
    }
    

    如果您不这样做,那么您需要创建自己的indexOf方法,然后自己遍历LinkedList

  2. 您的remove方法实际上并未执行删除操作,只需将该值设置为null。

    tempPair = bucketArray[key.hashCode()].get(tempNodeIndex);
    V returnValue = tempPair.value;
    tempPair.value = null;
    

    更正确的是:

    tempPair = bucketArray[key.hashCode()].remove(tempNodeIndex);
    return tempPair.value;
    
  3. 据我所知,你根本不需要Word课程。您对String的投射会假设K的类型是什么,这对于泛型类来说是可疑的。 (如果我有MyMap<Long, Double>怎么办?)

    您只是用它来获取hashCode已经拥有的K(因为在hashCode上声明java.lang.Object)。

    您可以使用上面定义的临时hashCode中的MyEntry或直接调用它:

    int keyMod = ( key == null ? 0 : key.hashCode() ) % slots;
    
  4. 要让Word班级正常运作,您需要正确覆盖hashCode

    // now you can call hashCode() on a Word
    // when a Word is passed in to MyMap as a key
    @Override
    public int hashCode() {
         int code = 0;
         // 'word' now refers to the instance variable
         for ( int i =0; i<word.length(); i++){
            code = word.charAt(i) + code;
         }
    
         return code;
    }
    
    // also implementing equals correctly, but your
    // implementation in the question probably did
    // not cause an error
    @Override
    public boolean equals(Object o) {
        if(!(o instanceof Word))
            return false;
        String passedIn = ((Word)o).word;
        boolean returnValue = word.equals(passedIn);
        return returnValue;
    }
    

    然后您就可以使用MyMap<Word, Integer>

答案 1 :(得分:0)

我在MyMap类中发现了一些我没有正确构建的问题。我找到了错误并修复了它。我的测试也导致了错误。我纠正了他们。我的Word课程没有问题。一旦我纠正了这些正确构建的地图,所有方法都有效。