It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
OK so Searching is my weak area in Java and could really use help as to where to begin on this Assignment!!
The data member n is no longer needed.
The LinkedList should be created and assigned to theList in the constructor rather than in run().
makeScanner(), getPerson(), and main() should not be modified. The Person and FileFormatException classes should also not be modified.
display() will no longer compile since theList is no longer an array. You can either change it to use a foreach or simply remove it.
run() has a loop that adds Person objects to the array. Change it so that it adds these to a list instead. Consider:
theList.add(p);
The variables index and n are no longer necessary.
Modify search() to perform the linear search on a list rather than an array. The easiest way is to use a foreach and return the correct Person if found. If the correct Person is not found, it should return null as before.
This is what I have so far:
import java.util.LinkedList;
import java.io.IOException;
import java.net.URL;
import java.util.Scanner;
public class ContactList {
private LinkedList<Person> theList;
private int n; // the number of Persons in theList
private Scanner keyboard;
public ContactList() {
keyboard = new Scanner(System.in);
} // no-arg constructor
// Returns a Scanner associated with a specific text-based URL
// online.
private Scanner makeScanner() throws IOException {
final String source =
"http://userpages.umbc.edu/~jmartens/courses/is247/hw/05/05.txt";
final URL src = new URL(source);
return new Scanner(src.openStream());
} // makeScanner()
// Return a Person instance based upon data read from the given
// Scanner.
private Person getPerson(final Scanner in) throws FileFormatException {
if (!in.hasNextLine())
return null;
String line = in.nextLine().trim();
int key = Integer.parseInt(line);
String name = in.nextLine().trim();
String mail = in.nextLine().trim().toLowerCase();
if (in.hasNextLine()) {
String empty = in.nextLine().trim(); // skip blank line
if (empty.length() > 0)
throw new FileFormatException("missing blank line");
} // if
return new Person(key, name, mail);
} // getPerson()
// Display the array contents.
private void display() {
for (int i = 0; i < n; ++i)
System.out.println(theList[i]);
} // display()
// Read the Person objects from the web page and then start the user
// interface.
private void run() throws IOException {
theList = new Person[1024];
try {
Scanner in = makeScanner();
int index = 0;
Person p = getPerson(in);
while (p != null) {
theList[index++] = p;
p = getPerson(in);
}
n = index;
} catch (IOException e) {
System.err.println("Error reading web page: " + e);
System.exit(1);
// The call to exit may be overkill, but it is nice to return an
// error (nonzero) value to the environment. Since main() does
// nothing after run() returns, simply returning to main() would
// be acceptable also. Perhaps the easiest way to do this is to
// simply move the call to ui() below into the try block. Then if
// an exception is thrown, the UI never executes.
} // catch
// Run the user interface.
ui();
// display();
} // run()
// Loop prompting the user for an integer key. Terminate on a negative
// key. If a record matching the key is found, display the
// record. Otherwise, indicate that no matching record was found.
private void ui() {
int key = getKey();
while (key >= 0) {
Person p = search(key);
if (p == null)
System.out.println("No person matching key "
+ key
+ " found.");
else
System.out.println(p);
key = getKey();
} // while not done
} // ui()
private int getKey() {
System.out.print("\nPlease enter a key: ");
int key = keyboard.nextInt();
return key;
} // getKey()
private Person search(final int key) {
for (int index = 0; index < n; ++index)
if (key == theList[index].getId()) // Is this the right one?
return theList[index];
return null; // apparently the requested object is not present
} // search()
public static void main(String[] args) throws IOException {
ContactList cl = new ContactList();
cl.run();
} // main()
} // class ContactList
The first thing I would do is change your list declaration! (like you said)
change:
private Person[] theList;
to
private LinkedList<Person> theList;
Then use your compiler to print all your compilation errors or look at all the red squiggles produced in your ide.
At each point where there is a compilation error or red squiggle, determine what array operation you are attempting. Then search on this page for the correct operation or sequence of operations that are equivalent. http://docs.oracle.com/javase/6/docs/api/java/util/LinkedList.html
http://www.java2s.com/Code/Java/Collections-Data-Structure/Useforeachlooptogothroughelementsinalinkedlist.htm
This is an example of using a for each statement to link through a simple list. You should change your declaration from an array to a linked list and try something similar to the for each in the above example.
http://docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
More reading on the subject if you need a little more background.
Related
This is the Main class with main method for generating a double-ended list, remove and display its elements.
public class Main {
Link first, last;
public static void main(String args[]) {
Main ob = new Main();
Link arr[] = {
new Link(1), new Link(2), new Link(3)
};
int len = 3;
for(int i=0;i<len;i++)
ob.insertFirst(arr[i]);
System.out.print("Data in the list: ");
while(ob.first!=null)
System.out.print(ob.removeAndReturn()+", ");
for(int i=0;i<len;i++)
ob.insertLast(arr[i]);
System.out.print("\nData in the list: ");
while(ob.first!=null)
System.out.print(ob.removeAndReturn()+", ");
}
void insertFirst(Link arg) {
if(isEmpty())
last = arg;
arg.next = first;
first = arg;
}
// This removeAndReturn() method returns the Object data the link is holding and removes that Link from the list
Object removeAndReturn() {
Object ret = null;
try {
ret = first.data;
if(first.next==null)
last = null;
first = first.next;
}catch(NullPointerException NPe) {
System.out.println("You are referring to a null.\nLinked List is empty.");
}
return ret;
}
void insertLast(Link arg) {
if(isEmpty())
first = arg;
else
last.next = arg;
last = arg;
}
boolean isEmpty() {
return first==null;
}
}
class Link {
Object data;
Link next;
Link(Object data) {
this.data = data;
}
}
When executing, it gives the following output:
Data in the list: 3, 2, 1,
Data in the list: 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, ... {truncated}
Here the last two elements gets repeated in the output. I tried nullifying both the Link variables first and last before calling ob.insertLast(arr[i]) but it gives the same output.
Update:
private keywords are removed from the complete method signature for methods in the Main class other than main(String args[]) method and rmF() method is changed to removeAndReturn().
The main problem in your code lies in the fact that you're using the exact same nodes (the ones within the arr) to fill your list with head and tail insertions.
In fact, once you perform your first head insertion, those nodes have been linked to each other like this:
(3) => (2) => (1) => null
So, when you're performing your second insertion, you have that node 1 points to node 2, node 2 points to node 3, and theoretically node 3 should point to null since it's supposed to be the last element. However, node 3's next field is still pointing to node 2 from the previous insertion. This creates a loop where node 2 and node 3 keep pointing at each other; thus yielding the infinite loop you're experiencing.
(1) => (2) => <= (3)
To fix your problem you could either reset the next field of your nodes before re-using them (poor solution) or work with the actual data you need to structure rather than the nodes. In fact, the user of your class shouldn't be bothered with the details of your implementation and should only care about the info to be stored/represented (in your case int numbers).
This is a possible solution to your problem:
public class Main {
Link first, last;
public static void main(String args[]) {
Main ob = new Main();
//Array of int not of links
int[] arr = {1, 2, 3};
int len = 3;
for (int i = 0; i < len; i++)
ob.insertFirst(arr[i]);
System.out.print("Data in the list: ");
while (ob.first != null)
System.out.print(ob.removeAndReturn() + ", ");
for (int i = 0; i < len; i++)
ob.insertLast(arr[i]);
System.out.print("\nData in the list: ");
while (ob.first != null)
System.out.print(ob.removeAndReturn() + ", ");
}
//------ CORRECTION ------
//The method should receive the info the user needs to store.
//It will then be up to you to represent it as a Link or whatever
//internal structure you're going to use tomorrow. Don't bind the
//user to your internal implementation.
//------------------------
void insertFirst(int arg) {
//Generating a new node (or link) based on the given info
Link l = new Link(arg);
if (isEmpty())
last = l;
l.next = first;
first = l;
}
// This removeAndReturn() method returns the Object data the link is holding and removes that Link from the list
Object removeAndReturn() {
Object ret = null;
try {
ret = first.data;
if (first.next == null)
last = null;
first = first.next;
} catch (NullPointerException NPe) {
System.out.println("You are referring to a null.\nLinked List is empty.");
}
return ret;
}
//-------- CORRECTION --------
//same explanation given above
//----------------------------
void insertLast(int arg) {
//Generating a new node (or link) based on the given info
Link l = new Link(arg);
if (isEmpty())
first = l;
else
last.next = l;
last = l;
}
boolean isEmpty() {
return first == null;
}
}
Lastly, Do not capture RuntimeException. These are unchecked exceptions, not checked. You should investigate on their origin rather than simply catching them. What you've written is a bad practice.
https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html
As #JayC667 has already said, you could improve some designing and naming of your class, methods and variables. There are some conventions, especially when talking about data structures. For example:
Your class is called Main but it describes a List, a name like MyList would have been better.
Your utility class, Link, could have been placed within MyList as a static nested class and probably named Node (it's a better fit).
Some of your methods' names were a bit too cryptic. Self-explanatory names will better help the users of your class.
Avoid accessing the internal state of another object from outside (list.first != null). Methods should be your way to go to interrogate an object's state.
Using generic types could have been a better implementation than just Object as generics provide: strict checks at compile time, avoid casting a more type safety, the ability to re-use the same code for different data types.
https://docs.oracle.com/javase/tutorial/java/generics/why.html
Here is a link to an implementation with the suggestions made above:
https://www.jdoodle.com/iembed/v0/s7C
I am trying to make a to do list that asks you to enter your tasks one by one then display them in order (as in 1. task1, 2. task 2, 3. task 3 etc). But when it displays the tasks it comes back as "0. null" one time instead of listing any of the tasks entered. Here is the script I am using:
1st class
package todolist;
import java.util.ArrayList;
public class ToDoList1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<ToDoList2> list = new ArrayList<ToDoList2>();
System.out.println("Time to make a digital to-do list!");
ToDoList2 n = new ToDoList2();
list.add(n);
System.out.println(ToDoList2.name + "'s to-do list");
System.out.println(ToDoList2.i + ". " + ToDoList2.l);
for(ToDoList2 enhanced : list)
{
System.out.println(ToDoList2.i + ". " + ToDoList2.m);
}
}
}
2nd class
package todolist;
import java.util.Scanner;
public class ToDoList2 {
public static String name;
public static int i;
public static String l;
public static String m;
{
Scanner s = new Scanner(System.in);
System.out.println("First type your name to identify your list in case you lose it");
name = s.nextLine();
System.out.println("Make sure to type \"end\" when you are finished");
System.out.println("Type in the first item on your to-do list");
String l = s.nextLine();
}
public ToDoList2()
{
Scanner s = new Scanner(System.in);
for(int i = 1; i == i; i++)
{
System.out.println("Type in the next item for your to-do list");
String m = s.nextLine();
if("end".equals(m))
{
break;
}
}
}
}
Your code is not correct. ToDoList2 scanning item list from standard input but not storing it. You should do as follow
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
class TodoList {
public static String name;
List<String> tasks;
public TodoList(String name) {
this.name = name;
this.tasks = new ArrayList<>();
}
public void addTask(String task) {
this.tasks.add(task);
}
public String toString() {
int i = 1;
StringBuilder stringBuilder = new StringBuilder();
for (String task : tasks) {
stringBuilder.append(i + ". " + task);
stringBuilder.append("\n");
i++;
}
return stringBuilder.toString();
}
}
public class Main {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("First type your name to identify your list in case you lose it");
String name = s.nextLine();
System.out.println("Make sure to type \"end\" when you are finished");
System.out.println("Type in the first item on your to-do list");
TodoList todoList = new TodoList(name);
String task = null;
while (!(task = s.nextLine()).equals("end")) {
todoList.addTask(task);
System.out.println("Type in the next item for your to-do list");
}
System.out.println(todoList);
}
}
a) Given that each ToDoList2 object is a separate task, I'm not sure why you've made the object class members static?
b) In your ToDoList2 constructor, you've got a for loop that introduces a local variable i which hides the ToDoList2 member variable i. You'd do well to change one of the variable names.
c) In your ToDoList2 constructor, you've got a for loop which is assigning a string returned by the Scanner to a local variable m. Are you sure you want m to be a local variable or do you actually want to assign the returned string to the member variable, m? I'm thinking the latter since the member variable m is never being assigned a value which explains why the code is printing out null.
d) When writing code, it is good practice to use meaningful variable names. Using names like i is OK as an index in a loop but in all other circumstances, you should go for something more descriptive that tells the reader what the variable is storing.
e) Consider making all your ToDoList2 member variables private (and final if possible). Add a print function to the ToDoList2 class to print out the task details. A key principle is Object Oriented Programming is to hide the internals of a class.
i'm writing a program for a game called 'Trivia'. Below is the source code:
Trivia.java
public class Trivia implements Serializable {
private String question;
private String answer;
private int points;
public Trivia() {
question = " ";
answer = " ";
points = 0;
}
public String getQuestion() {
return question;
}
public String getAnswer() {
return answer;
}
public int getPoints() {
return points;
}
public void setQuestion(String q) {
question = q;
}
public void setAnswer(String a) {
answer = a;
}
public void setPoints(int p) {
points = p;
}
}
Driver.java
public class Driver {
public static void main(String[] args) {
Trivia[] t = new Trivia[5];
for (int i = 0; i < 5; i++) {
t[i] = new Trivia();
}
t[0].setQuestion("How many states are in the US?");
t[0].setAnswer("50");
t[0].setPoints(1);
t[1].setQuestion("Who is the richest person in the US");
t[1].setAnswer("You");
t[1].setPoints(1);
t[2].setQuestion("How many senators come from each state?");
t[2].setAnswer("2");
t[2].setPoints(2);
t[3].setQuestion("What is the largest state?");
t[3].setAnswer("Alaska");
t[3].setPoints(2);
t[4].setQuestion("Who was the thrid president?");
t[4].setAnswer("Thomas Jefferson");
t[4].setPoints(3);
ObjectOutputStream outputStream = null;
try {
outputStream = new ObjectOutputStream(new FileOutputStream("C:\\Work\\workspace\\aman\\src\\trivia\\trivia.dat"));
} catch (IOException e) {
System.out.println("Could not open file");
System.exit(0);
}
try {
outputStream.writeObject(t);
outputStream.close();
} catch (IOException e) {
System.out.println("Writing error");
System.exit(0);
}
ArrayList<Trivia> triviaQuestions = new ArrayList<Trivia>();
try {
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("C:\\Work\\workspace\\aman\\src\\trivia\\trivia.dat"));
for(int i=0; i<5; i++){ // Repeats the content of the loop five times
triviaQuestions.add((Trivia) inputStream.readObject());
}
inputStream.close(); // Closes the input stream because it is not longer needed
} catch (IOException | ClassNotFoundException e) {
System.out.println("File not found.");
System.exit(0);
}
Trivia yourRandomTrivia = triviaQuestions.get((new Random()).nextInt(triviaQuestions.size())); // This will be your random question
}
// You did not get an auto complete suggestion because you typed outside of a method
}
noe when I try to run this program, I get an error saying "Ltrivia.Trivia; cannot be cast to trivia.Trivia". The error is thrown in class Driver on line " triviaQuestions.add((Trivia) inputStream.readObject());". I did some research on this and found that 'L' means array of a datatype. But, I have simple created an arrayList of type Trivia and trying to add each element I get from the inputStream by casting them to Trivia class.
Does anybody have any suggestions on this?
Your code is writing an Array of Trivia objects.
Then you try to read and add that to a list of Trivia objects.
You cant add arrays of Trivia to an List of Trivia!
And that is what the message is telling you: you cant cast the type Trivia[] to Trivia. Because an array of X is not the same as a single X.
One solution: instead of writing t as a whole, you can simply iterate t and write the members of the array. Of course that means that you have to somehow remember how many elements you wrote into that stream. You could get there by first writing an Integer object representing the number of Trivia objects that will follow.
The other solution: just read back that Trivia[]; and iterate it then; to add the various Trivia objects one by one.
Edit: on your comment: when you read from an ObjectInputStream you get back those things that you put into your file/stream earlier on. As said: your code puts a single object of type ARRAY of Trivia into bytes ... and then you want to read that thing back as a single Trivia object! That does not work!
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);
}
}
}
Sorry if this is answered somewhere due to me missing something obvious, but I've been googling this for days now and it just doesn't seem to make any sense. I've got 3 years of experience in Javascript and am getting into Java now, so I'm not behind on the basic concepts of anything and such.
I'm using IntelliJ for this, but it fails to point out the problem. The communication (access rights and instantiations) between my classes is fine, the code syntax and variable types are as well, etc, so I really can't tell what it is.
I have a Data class, which just holds "read-only" data for the other classes to use.
public class Data {
// snip
public static int[][] specs = {
{6,1,6,40},
{5,2,5,30},
{5,3,4,40},
{4,4,3,60}
};
}
There's another class that has to read this data when it's initialized.
public class Soldier {
// snip
public int range;
public Soldier() {
int x = ...; // user input
range = Data.specs[x][1];
}
}
The specs array itself contains its data as defined (ie the array is not empty), x is valid as an index of the specs array (ie 0 <= x <= 3), its type is int and Test has read access to the specs array (all confirmed with debug output statements). And yet, when it tries to set the value of range (then and only then, at that exact point), I get the "Index out of bounds" error.
Can someone please tell me what's going wrong when trying to read the array? Or am I right in saying that this is really weird and I need to post the entire code?
Note: a small new test also shows that, if I change the code to first output a manually chosen value from the array and then set the value of range, the console prints the error statement (and exits the program) and follows it up by printing the manually picked value, but assigning the value and then asking to output range only throws the error... That makes absolutely no sense at all!
Edit: I've edited the code above. The class called Test is called Soldier in my code (I'm making a text-based game...). Below's the stack trace, if it's any good without the full code (which is way long). The basic structure of my program is this:
1) Boot contains the main method and instantiates a new Game
2) Game instantiates x Teams
3) each Team instantiates an Army
4) each Army instantiates x Soldiers
Each instance of the classes is set as an attribute of the instantiating class (public Army army; and an Army instantiation in the Team constructor, for example). It's essentially a cascade of constructors instantiating subsequent classes and assigning them as their attributes.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at Army.<init>(Army.java:13)
at Team.<init>(Team.java:19)
at Game.<init>(Game.java:22)
at Boot.main(Boot.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)5
Edit edit: here's the semi-full code (I'm leaving out the stuff that has absolutely nothing to do with it, including the imports). It's in no particular order and the classes are in separate .java files within the IntelliJ project. The game continues up to the point where a new Soldier asks for its type to be designated (the function performing the user input is working fine and validating the input as proven by a technically identical other part of the game).
public class Boot {
public static void main(String[] args) {
Object[] games = new Object[] {};
if (Lib.userConfirmPrompt("Start the game?") == true) {
do {
games[games.length] = new Game();
}
while (Lib.userConfirmPrompt("Do you want to play again?") == true);
}
System.exit(0);
}
}
public class Game {
public Object[] teams = new Object[] {};
public Game() {
for (int i = 0;i < settings.xbots + 1;i++) {
teams[teams.length] = new Team(this);
}
}
}
public class Team {
public Game game;
public Army army;
public Team(Game p) {
game = p;
army = new Army(this);
}
}
public class Army {
public Team team;
public static Object[] soldiers = new Object[] {};
public Army(Team p) {
team = p;
for (int i = 0;i < team.game.settings.xsoldiers;i++) {
soldiers[soldiers.length] = new Soldier(this);
}
}
}
public class Soldier {
private Army army;
public int sight;
public int range;
public int distance;
public int damage;
public Soldier(Army p) {
army = p;
int type = Lib.userTxtIntOptionsPrompt(Data.isoldiertypes);
// HERE is where it crashes, type is assigned and valid but the array access fails
sight = Data.isoldierspecs[type][0];
range = Data.isoldierspecs[type][1];
distance = Data.isoldierspecs[type][2];
damage = Data.isoldierspecs[type][3];
}
}
public class Data {
public static List isoldiertypes = Arrays.asList("Scout","Private","Machinegunner","Grenadier");
public static int[][] isoldierspecs = {
{6,1,6,40},
{5,2,5,30},
{5,3,4,40},
{4,4,3,60}
};
}
public class Lib {
private static Scanner input = new Scanner(System.in);
// output
// default: 1 query string to print
public static void outBase(String query) {
System.out.print(query);
}
public static void outStd(String query) {
outBase(query + "\n");
}
// end of output
// input
// default: 1 query string to print,
// query and input are in-line (exception: userConfirmPrompt prints query block-wise and default instruction in-line before input),
// keeps user hostage until valid input is given (exception: userPrompt returns blindly)
public static String userPrompt(String query) {
outBase(query);
return input.nextLine();
}
public static String userTxtPrompt(String query) {
String menuinput = null;
do {
if (menuinput != null) {
userHostage();
}
menuinput = userPrompt(query);
} while (menuinput.length() == 0);
return menuinput;
}
public static int userIntPrompt(String query) {
String menuinput = null;
do {
if (menuinput != null) {
userHostage();
}
menuinput = userTxtPrompt(query);
} while(menuinput.matches("^-?\\d+$") == false);
return new Integer(menuinput);
}
// end of input
// options input
// default: takes a List of options as argument,
// prints an enumerated list of these options string-wise,
// prompts for a numeral selection of the desired option and returns the number if valid
public static int userTxtIntOptionsPrompt(List options) {
int choice = 0;
Boolean chosen = false;
do {
if (chosen == true) {
userHostage();
} else {
chosen = true;
}
chosen = true;
for (int i = 0;i < options.size() - 2;i++) {
outStd((i + 1) + ") " + options.get(i) + ",");
}
outStd((options.size() - 1) + ") " + options.get(options.size() - 2) + "\nand " + options.size() + ") " + options.get(options.size() - 1) + ".");
choice = userIntPrompt("Enter the number of the option you'd like to select: ") - 1;
} while(choice < 0 || choice >= options.size());
return choice;
}
// end of options input
// miscellaneous
public static void userHostage() {
outStd("Invalid operation. Please try again.");
}
}
The problem is in your Army class:
public static Object[] soldiers = new Object[] {};
You initialize an empty (length == 0) array named soldiers, but later you access:
soldiers[soldiers.length] = new Soldier(this);
This causes the failure.
By definition, soldiers.length is out of the bound of the array (since the bound is from 0 to soldiers.length-1)
To overcome it - make sure you allocate enough space in the array soldiers or use a dynamic array (ArrayList) instead. You can append elements to an ArrayList using ArrayList.add(), and you don't need to know the expected size before filling it up.
The x should be greater than -1 and less than 4.
The stacktrace does not mention the Solder class, its in the conctructor of the Army class.
Any how, only knowing that the index should be within a range is not enough. As a programmer its your duty to validate the index before trying to access an element at that index.
if(index > 0 && index < array.length) {
//then only acess the element at index
Problem is the array soldiers is of size 0.
This line int x = ...; // user input implies that you are taking input in some fashion from the user and accessing the array with it. Are you checking this value to see that is in range (i.e., between 0 and 3)? If not, this may be why your testing works.
Edit: something like this might solve it for you:
public class Army {
public Team team;
public Vector<Soldier> soldiers;
public Army(Team p) {
soldiers = new Vector<Soldier>()
team = p;
for (int i = 0;i < team.game.settings.xsoldiers;i++) {
soldiers.add(new Soldier(this));
}
}
}
Judging by your other code, this sort of pattern will be useful in your Game object as well.