0

The project I am working on right now involves me reading words from a text file and loading them into an array (and eventually a binary tree, but that will be finished later). I must load both the word and the word's frequency (initially 1) into the array, so I have packed both variables into an object WordNode. I am able to load the words into the array, but things fall apart when I try to check if a word is already in the array. If it is, I must increase the frequency by 1. I have written a new equals() method to override the default method, so that words are compared rather than variable references. However, I am now getting the error Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [LWordNode;. Why is this coming up? I thought only WordNode objects were being passed to equals().

Main method:

public class Driver {
/////////////// fields ///////////////
public static ArrayUnorderedList<WordNode> wordArray = new ArrayUnorderedList<WordNode>();
public static LinkedBinarySearchTree<WordNode> wordTree = new LinkedBinarySearchTree<WordNode>();   //tree to hold words

/////////////// methods ///////////////
public static void main(String[] args) throws Exception {
    //ask for filename       
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    System.out.println("Enter the name of the file to read from: ");
    Reader file = new FileReader(reader.readLine());

    //read file
    Scanner input = new Scanner(file);

    while(input.hasNext()) {
        //get words from file
        String word = input.next();

        //remove non-word characters and convert to lowercase
        word = word.replaceAll("\\W", "");
        word = word.toLowerCase();

        //create node
        WordNode newWord = new WordNode(word);

        //if word is already in array
        if(wordArray.contains(newWord)) {
            System.out.println("Word is already in array");

            //increment frequency by 1
            int index = wordArray.find(newWord);
            wordArray.list[index].setFrequency(wordArray.list[index].getFrequency() + 1);
            System.out.println("(" + newWord.getFrequency() + ") " + newWord.getWord());
        } else {
            System.out.println("Word is not yet in array");

            //add word to tree
            System.out.println("(" + newWord.getFrequency() + ") " + newWord.getWord());
            wordArray.addToRear(newWord);
        }
    }

    //insert into tree

    //perform traversals on tree
}

WordNode class:

public class WordNode {
   protected String word;
   protected WordNode left, right;
   protected int frequency;

   /**
    * Creates a new node with the specified data.
    * @param obj the element that will become a part of the new node
    */
   WordNode(String obj) {
      word = obj;
      left = null;
      right = null;
      frequency = 1;
   }

   /**
    * Method to check for string equivalence.
    * @return true if words are the same
    */
   public boolean equals(Object obj) {
       WordNode tempWord = (WordNode)obj;
       return(tempWord.getWord().equals(this.word));
   }

   /**
    * Gets the word.
    * @return the word
    */
   public String getWord() {
      return word;
   }

   /**
    * Sets the word.
    * @param word the word to set
    */
   public void setWord(String word) {
      this.word = word;
   }

   /**
    * Gets the left.
    * @return the left
    */
   public WordNode getLeft() {
      return left;
   }

   /**
    * Sets the left.
    * @param left the left to set
    */
   public void setLeft(WordNode left) {
      this.left = left;
   }

   /**
    * Gets the right.
    * @return the right
    */
   public WordNode getRight() {
      return right;
   }

   /**
    * Sets the right.
    * @param right the right to set
    */
   public void setRight(WordNode right) {
      this.right = right;
   }

   /**
    * Gets the frequency.
    * @return the frequency
    */
   public int getFrequency() {
      return frequency;
   }

   /**
    * Sets the frequency.
    * @param frequency the frequency to set
    */
   public void setFrequency(int frequency) {
      this.frequency = frequency;
   }
}

Some methods from the ArrayList class:

/**
* Returns true if this list contains the specified element.
* @param target the element that the list is searched for
* @return true if the target is in the list, false if otherwise 
*/
public boolean contains(T target) {
    return (find(target) != NOT_FOUND);
}

/**
* Returns the array index of the specified element, or the
* constant NOT_FOUND if it is not found.
* @param target the element that the list will be searched for
* @return the integer index into the array containing the target element, or the NOT_FOUND constant
*/
public int find(T target) {
    int scan = 0, result = NOT_FOUND;
    boolean found = false;

    if (!isEmpty()) {
       while (!found && scan < rear) {
          if (target.equals(list[scan])) {
              found = true;
          } else {
             scan++;
          }
       }
    }

    if (found) {
       result = scan;
    }

    return result;
}
lollercopter
  • 111
  • 3
  • 12
  • The error is somewhere in the `ArrayUnorderedList` class. A stacktrace and the full code of that class would be helpful. – trutheality Apr 11 '12 at 22:08
  • 2
    When you get an exception, the stack trace reports the line on which the exception was thrown which helps reduce the part of the code you need to examine. – assylias Apr 11 '12 at 22:08
  • This is "too localized" and SO is not a "code review site", but **it's like any other** CCE: you are trying to cast an inconvertible type. Casting *cannot* and *does not* change a type. It only changes the "statically typed view" of a given object. Also, please use the tools available to debug :( –  Apr 11 '12 at 22:13
  • I apologize, I didn't know where else to ask and I'm not very familiar with debugging in Eclipse. I have looked at the stack trace (I think that's what it was - the line number in the class where the error was found?) and apparently the error is in the line `wordArray.list[index].setFrequency(wordArray.list[index].getFrequency() + 1);`. Thank you for working with me, I'm still learning. – lollercopter Apr 11 '12 at 22:37

5 Answers5

4

One possible cause is your equals method:

public boolean equals(Object obj) {
    //You need to check that obj is a WordNode
    //For example: if (obj instanceof WordNode) {...
    WordNode tempWord = (WordNode)obj; 
    return(tempWord.getWord().equals(this.word));
}

will throw an exception if obj is not a WordNode.

You should have a look at this discussion about how one should override equals and hashcode in Java.

Community
  • 1
  • 1
assylias
  • 321,522
  • 82
  • 660
  • 783
  • 1
    +1 for the answer as it will solve the issue. I would add though that he/she should find out why this comparison happens. I am not saying it cannot happen, but on such simple program, it is maybe a sign of another error. He could add a thread dump (`new Exception("Here*").printStackTrace()`) when this happens – Guillaume Polet Apr 11 '12 at 22:15
  • I have looked at the stack trace (I think that's what it was - the line number in the class where the error was found?) and apparently the error is in the line `wordArray.list[index].setFrequency(wordArray.list[index].getFrequency() + 1);`. Thank you for working with me, I'm still learning. – lollercopter Apr 11 '12 at 22:38
  • It is difficult to know what is causing the exception without a full stack trace (which should contain more than one line) or the code of ArrayUnorderedList. – assylias Apr 11 '12 at 23:01
1

When you use Generics, Java erases the type information and performs casts at runtime. This means that if any part of your code contains T[], it is compiled as Object[] instead.

Based on the references to wordArray.list[index].setFrequency() and wordArray.list[index].getFrequency(), it looks like you used T[] as the type for the list field of ArrayUnorderedList. This is not a correct use of Generics. Java will attempt to cast the Object[] list to WordNode[] before accessing the array's index. Casting one array type to another array type will always produce a ClassCastException.

Instead of accessing the list field directly, consider implementing a public T get(int index) method in ArrayUnorderedList. The use of the generic type T in the method signature is the proper way to implement object access in a generic collection. Here, the implicit cast occurs after the array access.

Alternately, consider using the java.util.ArrayList or java.util.HashSet classes instead. Both are generic collections.

MALfunction84
  • 500
  • 3
  • 12
  • That is true, `list` is of type `T[]` in `ArrayUnorderedList`! That's how it was written in the supplied code. I was not aware about type erasure, but thank you for the info. I will try to implement a get method for this. (Do I simply write `public T get()` and then include something in this method like what I originally wrote, such as `this.list[index]`? That is my understanding.) – lollercopter Apr 11 '12 at 22:41
  • Yes. If you changed `list` to be an `Object[]`, you will need to cast the result to the generic type T. `public T get(int index) {` `return (T) list[index];` `}` Apologies for the lack of linebreaks. – MALfunction84 Apr 17 '12 at 12:35
0

don't except the equals method should be call with always a WordNode parameter.

You have to verify the type of the parameter object

public boolean equals(Object obj) {
   if(!(obj instanceof WordNone)){
     return false;
   }
   // rest of equals method
}
0

It's never safe to assume that what is passed in the equals will always be an instance of the same object. You need to do some check like

public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    WordNode otherWord = (WordNode) obj;

    return(otherWord.getWord().equals(this.word));
}

Anyway, why don't you use a Set instead? It's the best data structure that you can use for your case.

Caesar Ralf
  • 2,203
  • 1
  • 18
  • 36
0

Since you only care about a word and its frequency why aren't you using a Map or a Set instead? The Key would be the word and the value the frequency. That way you simply call Map.contains(word) and you see if its already in and then do whatever you want to do with it.

Giannis
  • 5,286
  • 15
  • 58
  • 113