CAW
CAW

Reputation: 21

Understanding why "Array required, but string found" Error appears

    import java.io.File;
    import java.util.HashMap;
    import java.io.IOException;
    import java.util.Random;
    import java.util.Scanner;
    import java.util.Set;

    /**
    * MarkovModel.java Creates an order K Markov model of the supplied source
    * text. The value of K determines the size of the "kgrams" used to generate
    * the model. A kgram is a sequence of k consecutive characters in the source
    * text.
    *
    *
    */
    public class MarkovModel {

    // Map of <kgram, chars following> pairs that stores the Markov model.
    private HashMap<String, String> model;
    private String kgram;
    private int size;
    private String charAft;
    private Random kgramChooser;
    private Random charChooser;
    private String firstKgram;
    // add other fields as you need them ...


    /**
    * Reads the contents of the file sourceText into a string, then calls
    * buildModel to construct the order K model.
    *
    * DO NOT CHANGE THIS CONSTRUCTOR.
    *
    */
   public MarkovModel(int K, File sourceText) {
      model = new HashMap<>();
      try {
         String text = new Scanner(sourceText).useDelimiter("\\Z").next();
         buildModel(K, text);
      }
      catch (IOException e) {
         System.out.println("Error loading source text: " + e);
      }
   }


   /**
    * Calls buildModel to construct the order K model of the string sourceText.
    *
    * DO NOT CHANGE THIS CONSTRUCTOR.
    *
    */
   public MarkovModel(int K, String sourceText) {
      model = new HashMap<>();
      buildModel(K, sourceText);
   }


   /**
    * Builds an order K Markov model of the string sourceText.
    */
   private void buildModel(int K, String sourceText) {
      if(K < 0 || sourceText.length() < K) {
         throw new IOException();
      }

      for (int i = 0; i < sourceText.length() - K; i++) {
         String kgram = sourceText.substring(i, i + K) + "";
         String charAfter = model.get(kgram);
         if (charAfter == null) {
            charAfter = "";
         }
         char charAft;
         if (i < sourceText.length() - K) {
            charAft = sourceText.charAt(i + K);
         }

         model.put(kgram, charAfter + charAft);
      }

      int size = model.size();
      String[] kgram = model.keySet().toArray(new String[0]);
      String firstKgram = sourceText.substring(0, K);
      Random kgramChooser = new Random();
      Random charChooser = new Random();
   }


      /** Returns the first kgram found in the source text. */
   public String getFirstKgram() {
      return firstKgram;
   }


   /** Returns a kgram chosen at random from the source text. */
   public String getRandomKgram() {
      return kgram[kgramChooser.nextInt(size)];
   }


   /**
    * Returns the set of kgrams in the source text.
    *
    * DO NOT CHANGE THIS METHOD.
    *
    */
   public Set<String> getAllKgrams() {
      return model.keySet();
   }


   /**
    * Returns a single character that follows the given kgram in the source
    * text. This method selects the character according to the probability
    * distribution of all characters that follow the given kgram in the source
    * text.
    */
   public char getNextChar(String kgram) {
      String charAfter = model.get(kgram);
      if (charAfter == null) {
         return '\u0000';
      }
      return charAfter.charAt(charChooser.nextInt(charAft.length()));
   }


   /**
    * Returns a string representation of the model.
    * This is not part of the provided shell for the assignment.
    *
    * DO NOT CHANGE THIS METHOD.
    *
    */
   @Override
    public String toString() {
      return model.toString();
   }

   }

I'm truly an amateur at coding so please excuse the terminology that I may use but I'm getting the error listed below. The above code is from an assignment that I've been working on but why is this error appearing if kgram is just an array of strings? What would also be the best way to go about instantiating a MarkovModel in a separate file? Please don't post just an answer I need to understand why as well.

MarkovModel.java:104: error: array required, but String found return   
      return kgram[kgramChooser.nextInt(size)];

Upvotes: 1

Views: 1751

Answers (2)

CookingWithJava
CookingWithJava

Reputation: 151

In your code, you have a potential NullPointerException lurking around the corner in this part:

if(K < 0 || sourceText.length() < K) {
     throw new IOException();
}

What if parameter sourceText is provided as a NULL literal? You get null.length() which causes the NPE.

This code is unnecessary:

if (i < sourceText.length() - K) {
        charAft = sourceText.charAt(i + K);
}

because it is already covered by the evaluation expression within the for-loop:

for (int i = 0; i < sourceText.length() - K; i++) {

Thus this situation can never occur in your code.

For those readers that do not grasp the String[] kgram = model.keySet().toArray(new String[0]); part, read this.

You have a getter-method

public String getFirstKgram() {
  return firstKgram;
}

which returns the firstKgram value, but in the method:

private void buildModel(int K, String sourceText) 

you do not set this firstKgram value at all. Is that the desired behavior?

I can't determine the reference to the kgramChooser datatype in the getter-method:

public String getRandomKgram() {
  return kgram[kgramChooser.nextInt(size)];
}

because the datatype is not specified in the provided code-block. Guessing what kgramChooser.nextInt(size) does is impossible without the proper code for us to review.

You probably have mixed up local datatypes within the buildModel(int K, String sourceText) method with object member fields String[] kgram, String firstKgram, Random kgramChooser and Random charChooser. If this is the case, remove the datatype at the beginning of each lines, so the object member fields are referenced instead.

Thus this code:

String[] kgram = model.keySet().toArray(new String[0]);
String firstKgram = sourceText.substring(0, K);
Random kgramChooser = new Random();
Random charChooser = new Random();

changes to this code:

kgram = model.keySet().toArray(new String[0]);
firstKgram = sourceText.substring(0, K);
kgramChooser = new Random();
charChooser = new Random();

Assuming that the referenced datatypes are actually member fields within your object.

UPDATE:

Add an extra null pointer check in the buildModel() method, like this:

private void buildModel(int K, String sourceText) {
  if (sourceText == null) throw new NullPointerException("sourceText parameter is NULL literal");  

  if(K < 0 || sourceText.length() < K) {
     throw new IOException();
  }

  ...

In the constructor MarkovModel(int K, File sourceText), change the instantiation of the model into:

public MarkovModel(int K, File sourceText) {
      model = new HashMap<String, String>();

You don't have to, but it sure is better programming in the sense that you have the complete overview within the constructor what the Map actually holds as objects.

Also change the private member field model from HashMap to Map, because you should always program against an Interface, not an Implementation. Thus:

HashMap<String, String> model;

becomes:

Map<String, String> model;

You can improve your coding skills by reading a wonderful book called: "Effective Java, Second Edition" by Joshua Bloch.

It will save you from embarrassing moments here on StackOverflow ;)

If you follow my instructions mentioned above, you will find your solutions you are seeking.

BTW: don't forget the upvote and select the answer that best fits your question(s).

Good luck!

Upvotes: 0

lexicore
lexicore

Reputation: 43709

Most probably you have a string field named kgram in your class. In your buildModel method you have a local variable kgram which is a string array. But in the getRandomKgram method you don't refer to that variable, you refer to the kgram field of your class.

Upvotes: 1

Related Questions