NullPointerException while indexing variable String parameters - java

I'm new to the idea of using an ellipsis. I'm almost certain my error is caused by improperly declaring or initializing "String[] authors", but I don't know how to do this an still have my setAuthors method work.
import java.util.*;
public class Book {
private String[] authors; //I'm guessing this line should end with "= new String..."
//but not sure how to w/o specifying an array dimension
private int authorsSize;
//Receives variable # of String parameters and indices them into String[] "authors"
public void setAuthors(String... authors) {
authorsSize = authors.length;
for(int i=0;i<authorsSize;i++)
this.authors[i] = authors[i];
}
//getAuthors method:
public String getAuthors(){
String s = "";
authorsSize = authors.length;
for(int i=0;i<authorsSize;i++)
s = s+authors[i] + ", ";
printAuthors = s;
return s;
}

The simplest approach would just be to clone the array:
public void setAuthors(String... authors){
this.authors = (String[]) authors.clone();
}
After all, you're overwriting the previous data anyway, and you can't know the size before the method call. You don't need the authorsSize variable at this point - you've got the authors array which knows its own length.
(If you were able to use immutable collections, you wouldn't even need to bother cloning, of course.)
EDIT: As noted in comments, this method will throw an exception if you pass in a null reference. You shouldn't automatically decide that this is a situation you should "handle" - it's entirely legitimate to document that the parameter must not be null. I would suggest documenting the behaviour around nullity either way.
Indeed, if you do this you might also want to initialize your instance field like this:
private String[] authors = new String[0];
That way you always know you'll have a non-null reference in that field, so you can use it with impunity. I would prefer this over having to check for null on every use.

you never initialized authors your array .
you need to initialize it before you use it .
String[] authors = new String[size];
//but not sure how to w/o specifying an array dimension
The best way is to use an List implementing classes as they are dynamic Arrays. i.e., you dont need to specify the size.
List<String> authors = new ArrayList<String>();

You have to currect your setAuthors method as described below
public void setAuthors(String... authors) {
if (authors != null && authors.length > 0) {
authorsSize = authors.length;
authors = new String[authorsSize];
for (int i = 0; i < authorsSize; i++)
this.authors[i] = authors[i];
}else{
this.authors = null;
}
}

Because the declaration of "private String[] authors" is before that of "public void setAuthors(String... authors)", you can not use the format like "String[] authors = new String[authorsSize]". This will make the size of authors always be 0.
The better way is to use the dynamic initialization:
List authors = new ArrayList();
Then use this.authors.add(authors[i]) to pass parameters.

you could also adjust your code like so:
import java.util.*;
public class Book{
private String[] authors;
private int authorsSize;
public void setAuthors(String... authors){
//check for null, you could also set this.authors = new String[] if you prefer.
if(authors == null){
this.authors = null;
}else{
authorsSize = authors.length;
//also add this line...
this.authors = new String[authorsSize];
for(int i=0;i<authorsSize;i++)
this.authors[i] = authors[i];
}
}
public String getAuthors(){
if(authors == null){
return ""; //could also return null here if you would prefer
}
StringBuilder s = new StringBuilder();
authorsSize = authors.length;
for(int i=0;i<authorsSize;i++){
if(i > 0)
s.append(",");
s.append(authors[i]);
}
//printAuthors = s;
return s.toString();
}
}

Related

Why does my compiler say that my return statement is missing?

I put in the return statement for both cases, if and else
but it's still says that the return statement is missing
What's wrong with my code?
public static getNext(){
ArrayList<String> copy = new ArrayList<String>();
Random dice = new Random();
int rolls;
for(int x=0; x<i.length; x++){
copy.add(i[x]);
}
if(copy.size() < 1){
return "NONE";
}
else{
rolls = dice.nextInt(copy.size());
return copy.get(rolls);
copy.remove(rolls);
}
}
else{
rolls = dice.nextInt(copy.size());
return copy.get(rolls);
copy.remove(rolls); <--- UNREACHABLE
}
Your compiler probably missed the return statement due to unreachable line of code after the return.
Also, you haven't declared the returned class. There should be:
public static <return type> methodName(<parameters>) {
<body>
}
Q: How could I remove the copy.get(rolls) after returning it? Is there a way?
A: Basically the question is about the concept of functions. Function is a piece of code which performs some logic and then, based on it returns something. Return statement is the last thing that happens in the function.
You can also have a block of code which doesn't return anything, but takes parameters. These are called procedures. Anyway, in Java we call both of them: functions and procedures methods.
You have some misunderstood in your code :
First when you make return the method should return something
Second you can't specify any kind of statement after your return
Third you have to store your return value that you want to remove it and return it in a separate variable
Your code should look like this :
public static String getNext() {
//-------------^^------------return type
ArrayList<String> copy = new ArrayList<String>();
Random dice = new Random();
int rolls;
for (int x = 0; x < i.length; x++) {
copy.add(i[x]);
}
if (copy.size() < 1) {
return "NONE";
} else {
rolls = dice.nextInt(copy.size());
String s = copy.get(rolls);//<<----------put the val you want in separate variable
copy.remove(rolls);//<<-----------remove your val
return s;//<<-----------return your val
}
}
Note
Like #BackSlash mention in comment, it is useless to remove your val from your list, because it will not used after you get out of your method, so if you are using this List in another place you have to declare it outside your method for example :
private static ArrayList<String> copy = new ArrayList<String>();
public static String getNext() {
//ArrayList<String> copy = new ArrayList<String>();//<<<-------------useless position
....

Java HashMap lookup failing on identical key

I have a small bug probably stemming from my misunderstanding of HashMap and it's killing me. I've included a small snippet of test code that illustrates the problem.
I omitted the Prefix class for conciseness, but my prefixes are just arrays of words. They are immutable, so when they are constructed they clone an array of strings passed into the constructor. Hashcode() and equals() methods are implemented so the conditionals pass. Essentially the problem is that I can only dereference the suffix list using prefix1 and not prefix2 (it returns null in the latter case.
FYI, my Hashmap is simply declared as:
// Stores mappings between "prefixes" (consecutive word phrases) and "suffixes" (successor words).
private Map<Prefix, ArrayList<String>> prefixSuffixPairs;
Any help is appreciated.
ArrayList<String> suffixInList = new ArrayList<String>();
suffixInList.add("Suffix1");
suffixInList.add("Suffix2");
String[] prefixWords1 = new String[] {"big", "the"};
Prefix prefix1 = new Prefix(prefixWords1);
String[] prefixWords2 = new String[] {"big", "the"};
Prefix prefix2 = new Prefix(prefixWords2);
prefixSuffixPairs.put(prefix1, suffixInList);
if(prefix1.hashCode() == prefix2.hashCode()) {
System.out.println("HASH CODE MATCH");
}
if(prefix1.equals(prefix2)) {
System.out.println("VALUES MATCH");
}
ArrayList<String> suffixOutList = null;
suffixOutList = prefixSuffixPairs.get(prefix2);
suffixOutList = prefixSuffixPairs.get(prefix1);
public int hashCode() {
int result = 1;
for( int i = 0; i< words.length; i++ )
{
result = result * HASH_PRIME + words[i].hashCode();
}
return result;
}
public boolean equals(Prefix prefix) {
if(prefix.words.length != words.length) {
return false;
}
for(int i = 0; i < words.length; i++) {
if(!prefix.words[i].equals(words[i])) {
return false;
}
}
return true;
}
public boolean equals(Prefix prefix) {
That does not override Object#equals (and thus is not used by the HashMap).
You are merely providing an unrelated method of the same name (overloading) -- but you could call that from the one below:
Try
#Override
public boolean equals(Object prefix) {
The #Override is not strictly necessary, but it would have enabled the compiler to detect this problem if you had applied it to your first method (you get an error when your assertion to override is mistaken).

Java storing both line number and value from a file

I have a set of data that look like this.
1:2:3:4:5
6:7:8:9:10
I have manage to use array list to store the information using a delimiter of ":".
However i would like to store the information of their line numbers together in the array list.
class test
{
String items;
String linenumber;
}
Example:
test(1,1)
test(2,1)
test(6,2)
test(7,2)
Here is my current code.
Scanner fileScanner = new Scanner(new File(fname));
fileScanner.useDelimiter("\n");
int counter = 0; String scounter;
String test;
String events;
while(fileScanner.hasNext())
{
events = fileScanner.next();
scounter = Integer.toString(counter);
Base obj = new Base(scounter, events);
baseArrayList.add(obj);
}
fileScanner.close();
I have try using delimiter "\n" and then trying to split out the string and it is not very successful.
Any advice would be appreciated.
public void Base_Seperator()
{
String temp, temp2;
String[] split;
String days, events;
for(int i = 0; i < baseArrayList.size(); i++)
{
temp = baseArrayList.get(i).events;
temp2 = baseArrayList.get(i).days;
split = temp.split(":");
}
}
Despite the code in #Alex's answer that may solve your problem, your attempt is almost close to get what you want/need. Now you only need to create Test instances and store them in a container, usually a List. I'll add the necessary code to start this from your code:
//it is better to return the List instead of declaring it as a static field
public List<Test> Base_Seperator() {
//try to declare variables in the narrower scope
//String temp, temp2;
//String[] split;
//String days, events;
//this variable must be recognized in all the paths of this method
List<Test> testList = new ArrayList<Test>();
for(int i = 0; i < baseArrayList.size(); i++) {
//these variables should only work within the for statement
String temp = baseArrayList.get(i).events;
String temp2 = baseArrayList.get(i).days;
String[] split = temp.split(":");
//you have splitted the String by :
//now you have every element between : as an item stored in split array
//go through each one and create a new Test instance
//first, let's create the lineNumber variable as String
String lineNumber = Integer.toString(i+1);
//using enhanced for to go through these elements
for (String value : split) {
//now, let's create Test instance
Test test = new Test(value, lineNumber);
//store the instance in testList
testList.add(test);
}
}
//now just return the list with the desired values
return testList;
}
Not part of your question, but some advices:
There are plenty other ways to write code to achieve the same solution (take #Alex's answer as an example). I didn't posted any of them because looks like you're in learning phase, so it will be better for you to first achieve what you're looking for with your own effort (and a little of help).
Not sure if you're doing it (or not) but you should not use raw types. This is, you should always provide a generic type when the class/interface needs it. For example, it is better to define a variable as ArrayList<MyClass> myClassList rather than ArrayList myClass so the class become parameterized and the compiler can help you to avoid problems at runtime.
It is better to always program oriented to interfaces/abstract classes. This means, it is better to declare the variables as an interface or abstract class rather than the specific class implementation. This is the case for ArrayList and List:
List<String> stringList = new ArrayList<String>();
//above is better than
ArrayList<String> stringList2 = new ArrayList<String>();
In case you need to use a different implementation of the interface/abstract class, you will have to change the object initialization only (hopefully).
More info:
What is a raw type and why shouldn't we use it?
What does it mean to "program to an interface"?
Looks like you want to store days instead of lineNumber in your Test instances:
//comment this line
//Test test = new Test(value, lineNumber);
//use this one instead
Test test = new Test(value, days);
First of all you don't need to keep line number info in the test object because it can be inferred from the ArrayList that holds them. If you must though, it should be changed to an int. So,
class test
{
ArrayList items<Integer>;
int linenumber;
public test(int line, String[] input){
items=new ArrayList();
linenumber=line;
//populate with the line read by the Scanner
for(int i=0; i<input.lenth; i++)
items.add(Integer.parseInt(input[i]));
}
}
I use an ArrayList inside test because you don't know how many elements you'll be handling. Moving on to the scanner
Scanner fileScanner = new Scanner(new File(fname));
// fileScanner.useDelimiter("\n"); You don't need this!
String tmp[];
int line=0; //number of lines
while(fileScanner.hasNext()) {
line++;
//this returns the entire line, that's why you don't need useDelimeter()
//it also splits it on '.' I'm not sure if that needs to be escaped but
//just to be sure
tmp=fileScanner.nextLine() . split(Pattern.quote("."));
baseArrayList.add(new test(line, tmp));
}
fileScanner.close();
Here I use test to store the objects you read, I'm not sure what Base is supposed to be.
A Java Bean/construct is required that will hold the day and the item together. The following code will read the text file. Each line will be converted to a List where finally the application will populate the List DayItems collection properly.
public class DayItem {
private int day;
private String item;
public int getDay() {
return day;
}
public void setDay(final int day) {
this.day = day;
}
public String getItem() {
return item;
}
public void setItem(final String item) {
this.item = item;
}
}
And main code
public class ReadFile {
private static final List<DayItem> dayItems = new ArrayList<DayItem>();
public static void main(String args[]) throws FileNotFoundException{
final BufferedReader bufferReader = new BufferedReader(new FileReader("items.txt"));
int lineNumber=0;
try
{
String currentLine;
while ((currentLine = bufferReader.readLine()) != null) {
lineNumber++;
List<String> todaysItems = Arrays.asList(currentLine.split(":"));
addItems(todaysItems,lineNumber);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void addItems(final List<String> todaysItems,final int day){
int listSize = todaysItems.size();
for(int i=0;i<listSize;i++){
String item = todaysItems.get(i);
DayItem dayItem = new DayItem();
dayItem.setDay(day);
dayItem.setItem(item);
dayItems.add(dayItem);
}
}
}

String array cannot be resolved as variable

Is it possible in Java to redefine value after it already been defined(Like in JavaScript)? Take a look at my sample code, I am trying to redefine String array.
public String[] checkIfLengEnglish (){
String language = Locale.getDefault().getDisplayLanguage() ;
String LG = Locale.getDefault().getLanguage();
if(LG.contains("en")){
String language[] = {"English"}; // Redefining
}
else {
String Language[] = {"English/"+ Language,Language,"English"}; // Redefining
}
return Language[];
}
you re-define Language in your code with multiple types at multiple scopes (once at the method level, twice in the if-block/else-block). Don't do that.
You don't need to add the [] to reference an array variable, don't do that.
Since you declare the array inside the if-block, it only exists inside the if-block. To fix this, you need to declare it outside:
String[] languages;
if( LG.contains("en")){
languages = new String[] {"English"};
}else {
languages = new String[] {"English/"+ Language,Language,"English"};
}
return languages;
Since you no longer use initalization (which can only happen when you declare a variable) but assignment, you need to use the "long form" for specifying the array values, which includes new String[].
Also note that as a general guideline, method and variable names should start with a lower-case letter and class/interface/enum names should start with a capital letter. That's not technically required, but following this guideline will make your code easier to understand for others.
just reframing your code & variables for better understanding purpose
public String[] CheckIfLengEnglish (){
String displayLanguage = Locale.getDefault().getDisplayLanguage() ;
String LG = Locale.getDefault().getLanguage();
String arrayLanguages[];
if( LG.contains("en")){
arrayLanguages = {"English"};
}else {
arrayLanguages = {"English/"+ Language,Language,"English"};
}
return arrayLanguages ;
}
Language[] is defined inside your if/else statement. You should try putting it above that like
String[] array = new String[];
if(true){
array = {"English"};
}
return array;
// dummy example
String[] Language=new String[];
if
{
Language={"English"}
}
else {
String Language[] = {"English/"+ Language,Language,"English"};
}
return Language[] ;

Variable might not have been initialized when dealing with array

In a method I created I am trying to create is meant to return an array of user inputted strings. The issue that I am having it the compiler is saying that userData may not be initialized at userData[i]=tempData; and at return userData;. I am unsure why this error is occuring, and would like some feedback.
public String[] getStringObj() {
int i = 0;
String tempData;
String[] userData;
Boolean exitLoop = false;
System.out.println("Please list your values below, separating each item using the return key. To exit the input process please type in ! as your item.");
do {
tempData = IO.readString();
if (tempData.equals("!")) {
exitLoop=true;
} else {
userData[i] = tempData;
i++;
}
} while (exitLoop == false);
return userData;
}
In the interests of improving code quality:
You don't need that exitLoop flag; just do
while(true) {
String input = IO.readString();
if(input.equals("!")) {
break;
}
/* rest of code */
}
Since you seem like you want to just add stuff to an array without bounds, use an ArrayList instead of an array (added bonus, this gets rid of i too):
List<String> userData = new ArrayList<String>();
...
userData.add(line);
If you do these two things, your code will be much more concise and easy to follow.
Your userData is not initilaized and you are attempting to use it here userData[i]=tempData; before initialization.
Initialize it as
String[] userData = new String[20];
//20 is the size of array that I specified, you can specify yours
Also in your while condition you can have while(!exitLoop) instead of while(exitLoop==false)
You didn't initialize the String[]. Just do String[] userData = new String[length]. If you are unsure of the length, you may just want to use an ArrayList<String>.

Categories

Resources