Java ArrayList indexes lagging behind in for-loop? [duplicate] - java

This question already has answers here:
How do I compare strings in Java?
(23 answers)
Closed 4 years ago.
So I'm a scrub at java, just started learning the language at a university course.
While trying to compare values of variables linked to objects to inserted values to make sure that there are no duplicates, I ran into an issue. It appears as if the objects are "lagging behind" in the lack of a better expression. See the commenting of the code for a better explanation.
The problem only appears if you add like 2 or more separate weapons and then try to re-add a weapon with the same name, it allows you to do so. If you add just one one weapon and then try to re-add it it says nope and the code works as intended.
So yeah, when I call the method printHeroInfo I still see that I can end up with several weapons with the same name. There is also another class called superWeapon, and if you think the code in there is relevant to this issue then let me know and I'll post that in a commment or something.
Thanks in advance for helping me with the head scratching.
import java.util.ArrayList;
public class superHero{
private ArrayList<superWeapon> superWeaponList;
private String superHeroName;
private String superHeroInfo;
public superHero(String superHeroName, String superHeroInfo){
this.superHeroName=superHeroName;
this.superHeroInfo=superHeroInfo;
superWeaponList=new ArrayList<superWeapon>();
}
public void addSuperWeapon(String superWeaponName, int superWeaponCharges){
superWeapon Obj = new superWeapon(superWeaponName, superWeaponCharges); // Making an object of the superWeapon class.
int size=superWeaponList.size(); // Gets the size of the array superWeaponList.
if(size==0){ /* If the size of the arary is zero, that means there are no weapons in it currently.
For that reason we don't need the for-loop we otherwise would need to compare the name of the weapon
we are trying to add against the pre-existing names of weapons in the array. */
superWeaponList.add(Obj);
System.out.println("The Superweapon"+superWeaponName+" has been added for the superhero "+superHeroName+".");
System.out.println(superWeaponName+" has "+superWeaponCharges+" charges.");
}
else{ /* If the size of the array is NOT zero, we need to compare the name of the weapon we are trying to add
against the weapons already existing in the array. This we do with the following for loop. */
for(int i=0;i<size;i++){
superWeapon temp_obj=superWeaponList.get(i);
String temp_name = temp_obj.getName();
System.out.println(temp_name); /* Why does the name lag behind one entry?
On the second entry it also shows the first entry. On the third entry it shows the second
entry and so on... The first place in an arraylist should be index=0,
so the for-loop starting at 0 should also be correct, right?
I just added this print to see what the current value for getName()
would end up returning. This is how I found the bug/error. */
if(temp_name!=superWeaponName){ // If the name doens't match then we add a new superweapon.
superWeaponList.add(Obj);
System.out.println("The superweapon "+superWeaponName+" has been added for the superhero "+superHeroName+".");
System.out.println(superWeaponName+" has "+superWeaponCharges+" charges.");
}else{ // If the names match however, we don't add the new weapon we were trying to add.
System.out.println("There already is a superweapon with the name "+superWeaponName+
" registered for the superhero "+superHeroName);
}
}
}
}
public void printHeroInfo(){
System.out.println(superHeroName);
System.out.println(superHeroInfo);
int size=superWeaponList.size();
for(int i=0;i<size;i++){
superWeapon temp_obj=superWeaponList.get(i);
System.out.println(superHeroName+" has the superweapon "+temp_obj.getName()+" with "+temp_obj.getCharges()+" charges.");
}
}
}

Although it can be improved a lot with using Set instead of list, but I will just point out the error in assumption you are making in comparing Strings...
if(temp_name!=superWeaponName) //incorret
if(!temp_name.equals(superWeaponName)) //correct

Related

java: Two Possible int Values: Score Tracker for a Game

I'm working on a project to create a Space Invaders style game:
I need to be able to keep score when a player hits the target (+1 point) or misses (-1 point).
I will call the recordScore() method when the player shoots at the alien entity, however, I am unsure how to set it up so that my value (int point) can either be 1 or -1.
This is what I currently have:
public void recordScore(int point){
if(/*an alien ship is hit*/){
point = 1;
}
else{
point = -1;
}
I will call the method as such:
//if there is a hit
recordScore(1)
//if there is no hit
recordScore(-1)
...which I feel is a hint but I feel like using the loop is incorrect as I feel that would not work with how the method will be called but I can't currently think of any other way to initialize the variable to two values (or fill the if clause).
This is thinking backwards:
public void recordScore(int point){
if(/*an alien ship is hit*/){
point = 1;
}
else{
point = -1;
}
You are trying to assign a value to a method parameter which is the exact opposite of what you should be doing. Remember that point is coming into this method with a value assigned and passed into it.
Instead use the value that the parameter holds to assign to the value of a class field, or in your case to increment or decrement the value of a field.
So something like:
public void recordScore(int point) {
score += point; // similar to score = score + point;
}
is closer to what you need. I have no idea if you have a score field and you may name it something else.
Note that you state:
I feel like using the loop is incorrect
If you're referring to to the if/else structure, please understand that this is not a loop but rather a program control flow statement or logic block. A loop would make code repeat itself, and examples include for loops and while loops.
If you will only want to increment or decrement score by 1, another option is to use a method that uses a boolean parameter. For example
public void attemptHit(boolean hitSuccessful) {
if (hitSuccessful) {
score++;
else {
score--;
}
}
and you'd call it like:
attemptHit(true); // for a successful hit
attemptHit(false); // for a miss
This will prevent nonsense but valid code from being created such as
recordScore(-23020);

Struggling with conversions and using arrays to specify a random item to give to a user

I am currently learning to code in Java so any constructive criticism is much appreciated. (Please try to dumb things down a little. I'm not too sure on a lot of things as of yet. But I am getting there.
What I am trying to do at the moment is to create a plugin that when a command is run, a number 1-10 is generated and according to the array, that item will be given to the user. But there is something not working and not allowing me to change from String to Material. I know and understand the error. But don't know how it would be fixed. Thank you for the help! :)
package me.nubzz;
import java.util.Random;
import org.bukkit.Material;
import org.bukkit.command.*;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.inventory.*;
public class MysteryGift {
Random rnd = new Random();
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
String[] itemArray = {"Apple", "IronIngot", "GoldIngot", "Diamond", "Carrot", "IronShovel", "IronPickaxe", "IronSword", "IronAxe", "IronHoe"};
if (cmd.getName().equalsIgnoreCase("gift") && sender instanceof Player) {
int giftn = rnd.nextInt(10) + 1;
Player player = (Player) sender;
PlayerInventory inventory = player.getInventory();
String pItem = itemArray[giftn];
inventory.addItem(new ItemStack(pItem, 1));
return true;
}
return false;
}
}
The constructor ItemStack(String, int) is undefined
Is the error I receive.
inventory.addItem(new ItemStack(pItem, 1));
Is the line it is on.
Obviously, the ItemStack class does not have a constructor, which accepts a String and int parameter. You need to define such a constructor in the ItemStack class or use other parameter types.
There are a few possibilities here:
ItemStack does not have a constructor.
ItemStack does have a constructor, but not one that takes a String and int as parameters.
ItemStack does have a constructor that accepts these parameters, but they are in the wrong order (i.e., it takes int first and then String).
ItemStack is in another package which has not been imported.
An ItemStack in Bukkit is almost always instantiated using a Material "type" (or an ID, although that is deprecated). In your case, since the item amount is always one, you could use the ItemStack(Material mat) constructor to make an ItemStack with an amount of one to give to the player.
If you really want to save the types of items in String format you could use the Material.getMaterial(String name) method to retrieve the corresponding material for a given name, although you will have to use the built in Bukkit names for the materials (example: "IRON_SPADE" instead of "IronShovel").
Also note that the way you are creating the index that you use to retrieve a random element in the array will eventually result in an ArrayIndexOutOfBoundsException. The first element in an array has index zero. Your String array has ten elements, thus contains indices zero through nine. The method random.getInt(int) returns a pseudo-random integer between zero and the specified integer minus one (exclusive of the provided integer). Therefore rnd.nextInt(10) will return values between zero and nine (which is all you need for your array). Adding one to that index now makes the index have a value between one and ten, meaning it will never retrieve the first element and will eventually try to retrieve an element at index ten (which does not exist) and then throw an ArrayIndexOutOfBoundsException. I rewrote your method to give an example of how it could look, hope that helps:
public boolean onCommand(CommandSender sender, Command command, String alias, String[] args) {
if (command.getName().equalsIgnoreCase("gift") && sender instanceof Player) {
Player player = (Player) sender; // Cast player
int index = rnd.nextInt(10); // Pseudorandom value between 0 and 9
// The array with material enumerators (10 elements, indices from 0 to 9)
Material[] materials = {Material.APPLE, Material.IRON_INGOT, Material.GOLD_INGOT, Material.DIAMOND, Material.CARROT, Material.IRON_SPADE, Material.IRON_PICKAXE, Material.IRON_SWORD, Material.IRON_AXE, Material.IRON_HOE};
player.getInventory().addItem(new ItemStack(materials[index])); // Add a new ItemStack to the player's inventory using the selected material
}
return false;
}

Beginner Java Scope Issue [duplicate]

This question already has an answer here:
Beginner Java: Variable Scope Issue
(1 answer)
Closed 7 years ago.
I'm new to programming and seem to be running into issues with when a variable, class, etc can and can't be referenced. Below is an example, hoping one of you can fix the specific issue but also help me understand it more broadly so I don't run into it again and again.
to try and avoid posting a bunch of code please note that a Question class is defined as well as a setText, setAnswer, checkAnswer, and display method are all defined elsewhere (all public).
The relevant code is below and I have two questions:
Why is the variable first not recognized in the method presentQuestion()?
At the very end there, why can't I just call the method checkAnswer() on first, i.e. why can't I just do first.checkAnswer(response);? Why do I have to define it in a new variable: boolean outcome = first.checkAnswer(response);?
Code:
/**
* This program shows a simple quiz with two questions.
*/
public class QuestionDemo {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
Question first = new Question();
first.setText("Who was the inventor of Java?");
first.setAnswer("James Gosling");
Question second = new Question();
second.setText("Who was the founder of Udacity?");
second.setAnswer("Sebastian Thrun");
int score = 0;
score = score + presentQuestion(first, in);
// Present the second question
score = score + presentQuestion(second, in);
System.out.println("Your score: " + score);
}
/**
* Presents a question to the user and obtains a response.
* #param q the question to present
* #param in the scanner from which to read the user input
* #return the score (1 if correct, 0 if incorrect);
*/
public static int presentQuestion(Question q, Scanner in) {
// Display the first question
first.display();
System.out.println("Your answer:");
String response = in.nextLine();
// Check whether the response was correct
// If so, print "true" and return 1
// Otherwise, print "false" and return 0
boolean outcome = first.checkAnswer(response);
System.out.println(outcome);
if (outcome) {
return 1;
}
else {
return 0;
}
}
}
The reason you can't use the variable first inside presentQuestion is because it's defined in main, and therefore not visible outside of main. Isn't this precisely why you gave presentQuestion its Question q parameter, however?
It seems to me that this is what you want to do:
public static int presentQuestion(Question q, Scanner in)
{
// Display the first question
q.display();
System.out.println("Your answer:");
String response = in.nextLine();
// Check whether the response was correct
// If so, print "true" and return 1
// Otherwise, print "false" and return 0
boolean outcome = q.checkAnswer(response);
System.out.println(outcome);
if (outcome) {
return 1;
} else {
return 0;
}
}
Note that references to first have been replaced by references to q.
To try and clear up what I imagine your confusion may consist in, imagine if presentQuestion were called from another method than main, in which case no first variable would be declared at all. What would then happen to the references to first inside of presentQuestion, now not referring to anything at all? This is why you need to explicitly pass the data you want as parameters. Different methods are independent blocks of code, and you can't intermingle variable references between them even if they happen to call each other.
As for question 2, there should indeed be no problem with checking if(q.checkAnswer(response)) directly, without using the outcome variable. I'm guessing you were simply confused by the error emitted by the compiler when first wasn't recognized again.
first is a local variable, that means it can only be accessed inside the method in which it is defined.
You don't have to put the result of checkAnswer() into a boolean before using it. Actually, if (checkAnswer(response)) { ... } is valid.
presentQuestion takes a question as a parameter. In main, you're calling it on the first question, and then on the second question; it looks like the intent is that you use presentQuestion on the first question, and then on the second question. So far, so good.
The problem is that in presentQuestion, you're referring to the question (which could be the first or the second question) as q in the parameter list. All you need to do is use q instead of first in the rest of the method.
When I was new to programming, I had this problem as well! Then I found out it is very simple.
For your first question, first is declared in the main method and you want to use it in presentQuestion method. But presentQuestion and main are different methods! So you can't get to first in presentQuestion. As you can see, there is a Question-typed parameter in the presentQuestion method. This is like you telling first, "Come here, man! And then change your name to q." When you do pass the argument first to presentQuestion,
presentQuestion (first, in);
first comes to the pressentQuestion method with its name being as q. So you should use q instead of first in the presentQuestion method.
Now the second question, using a variable in this context is not needed. But to increase efficiency, use a boolean variable to store the result of checkAnswer. Let's imagine what happens if you don't use a boolean variable.
System.out.println(q.checkAnswer(response));
if (q.checkAnswer(response)) {
return 1;
} else {
return 0;
}
See? you called q.checkAnswer twice! This would slow down your program so you should use a boolean variable.

Interpreting data from an array java

I have an assignment that requires me to take a large dataset, store it in an array, and then create methods that interpret the data in various ways. The file data I am given is in the form like so:
0 138
0 139
0 140
0 141
0 142
0 799
4 1
4 10
4 12
4 18
etc... (it is very large)
This data is supposed to represent a social network of people, with the numbers representing individuals. Each line contains a person on the left who has 'trusted' the person on the right. I am supposed to interpret this data so that I can find all the persons a particular person trusts, how many people trust a particular person, and how to find the most trusted person. However, I am at a complete loss as to how to write these methods, and so I was wondering if you guys could help me out. Here's the code I have so far:
public class SocialNetwork {
static Scanner scanner = new Scanner(System.in);
static void findTrusted()
{
System.out.println("Please input person number you would like to find Trustees for");
trustee = (scanner.next());
}
public static void main(String[] args){
File inData = new File("dataset.txt");
ArrayList<Integer> links = new ArrayList<Integer>();
try
{
Scanner in = new Scanner(inData);
in.nextLine();
in.nextLine();
in.nextLine();
in.nextLine();
while (in.hasNext())
{
int trustee = in.nextInt();
int trusted = in.nextInt();
links.add(trustee);
links.add(trusted);
}
in.close();
}
catch (FileNotFoundException e){
e.printStackTrace();
}
}
}
As you can see, my findTrustee method has very little in it. I just don't know where to even start. I have come up with a little pseudocode to try and dissect what needs to be done:
prompt user for input on which person(integer) to find his/her trustees
search arraylist links for person(integer) inputted
print all persons(integers) on the right side of the lines that begin with person requested
However, I just don't quite know how to do this.
The structure links doesn't really help you. It has no idea of "from" and "to". You are storing Persons as numbers, but not storing any relationships between two people. You're really working in graph theory, and when you can you should look at reference works and Java libraries for graph theory.
So, what is a trust link? It is an object that has two people, the trustee and trusted people. Create a class for this:
public class Trust {
private final int trustee;
private final int trusted;
public Trust(final int trustee, final int trusted) {
this.trustee = trustee;
this.trusted = trusted;
}
// Getters, equals, hashCode, toString, formatted output for humans.
}
Have your class SocialNetwork be able to create these. By the way, create a SocialNetwork instance in your main method, and stop using static for everything else.
public Trust createTrust(Scanner scanner) {
int trustee = scanner.nextInt();
int trusted = scanner.nextInt();
return new Trust(trustee, trusted);
}
You might need to add exception handling and end of file handling.
Make links a list of Trust objects, and then write methods that scan that list as needed.
/**
Return a list of all the people who trustee trusts.
#param trustee A person in the system.
#return a list of the people trustee trusts.
*/
public List<Integer> trusting(int trustee) {
final List<Integer> trusted = new ArrayList<>();
for (Trust link: links) {
// Add something from link to trusted if it should.
// This looks like homework; I'm not doing everything for you.
}
return trusted;
}
Write other methods as you need them. Then, think about whether these data structures are efficient for this problem. Could Maps be better? MultiMaps from other libraries? An open source graph theory library of some sort? Perhaps you should use a database instead. Perhaps you should have a Person class instead of using just integers; that way you can label people with their names.
I think there are quite a number of ways you can implement this (regardless of performance). For example, you can use HashMap, array of array (or list of lists if you really like list...)
I will give an example using list maybe, since you seem using it... (although I think this is a bit odd)
Say, you have a list holding the people on the left.
ArrayList<ArrayList> leftList = new ArrayList<ArrayList>();
For leftList,loop through it till you reach the max no. of the left column (now you may see why an array/HashMap is better...) by doing something like:
leftList.add(new ArrayList());
in each loop.
Then all you have to do now is to read the file and plug the list of trustees to rightList corresponding to the truster. E.g. I have 1 3, 1 4 and 2 3; your implementation will achieve sth like:
leftList.get(1).add(3) / leftList.get(1).add(4) / leftList.get(2).add(3)
depending which line you are reading.
With this setup, I guess you can solve those three questions quite easily? Otherwise, just look for more advice here. But make sure you think through it first!
Hope my answer gives you some ideas.

How can I check where a variable gets updated/changed in my code (java - eclipse)?

I'm having some problems debugging my code as I can't work out where an array is getting updated. I have looked through all my class files and I'm completely stumped as to what is happening.
At the moment I have a setup whereby I read in a csv file and store the contents in a 2D array (public static double[][] myArray). I then pass this array into a method whenever a user presses a button.
The weird thing is that it works fine the first time, however on all subsequent clicks of the button the data has changed and I can't work out why. I have searched through the code and there are absolutely no references to the original array (i.e. myClass.myArray = newArray) apart from where I pass it into the method.
Is there a reason this is happening? Alternatively please could someone suggest a way to track when the variable gets updated/changed?
Here is a brief overview of the code...
Read in the csv dataset and save it in a variable (ClassA):
public static double[][] myDataset;
// ...
private static void readdDataset(String filePath)
{
CsvReader reader = new CsvReader();
myDataset = reader.readDataset(filePath, true);
}
This is the code used to set up the buttons' action:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
new RunProgram().execute(); // create a swing worker to run the code in bg
}
});
Here is a condensed down version the code in the SwingWorker class:
protected Void doInBackground()
{
Config cfgFile = someClass.createConfigFile();
someOtherClass.runMyProgram(cfgFile, ClassA.myDataset);
return null;
}
Finally here is the code to run the program:
public static void runRegression(Config cfgFile, double[][] dataset)
{
// Print out the first line in the array to see if it is the same every time (it should be!)
for(double value : dataset[0])
{
System.out.print( value + " ");
}
System.out.println();
// Do some other stuff....
}
Every method you pass this array to has the opportunity to change its contents. Your array is not immutable.
From the outline or project view, right-click on the variable, and select "Toggle Watchpoint".
(You can also set conditional breakpoints, which is handy.)
There doesn't need to be a reference to the original array, any reference to a static (mutable) object can manipulate its contents.
So I can't really be sure what is going on for sure since you seem to be dealing with 2d object and classes and I have a very basic understanding on them.
However it sounds like your problem aren’t really the 2d aspects but the storage of the data it uses. It also sounds like your button is the problem or it's before the button gets pressed and since you haven't given us the code I don't think anyone can help you.
Try making a temp bypass for the button so it just auto fires once and see what happens.
As for the debugging the normal way is to say
System.out.println("Array changed " + myArray[0] +" "+ myArray[1] );
This will print in a line on the console
Array changed 1 4 (if myArray[0] == 0 and myArray[1] == 4)
I can't help you anymore without seeing some code and I don't think anyone else can help.

Categories

Resources