java ConcurrentHashMap with constructor as key - java

I am currently trying to write a little game in java lwjgl/OpenGL.
When running the code i get the value NULL when reading from some ConcurrentHashMap.
I've written a simple program to reproduce the same issue and sure enough, i could.
Let me show the code to you:
The Program consists of three classes.
The Main class:
package main;
public class Main {
private MapContainer con = new MapContainer();
public static void main(String[] args) {
new Main();
}
public Main() {
ValueContainer vc = new ValueContainer(1, 2, 3);
this.con.set(vc, "Just a String");
System.out.println(this.con.get(vc));
}
}
Then there's the MapContainer class.
It's basically a class that contains a ConcurrentHashMap and two methods to access it:
package main;
import java.util.concurrent.ConcurrentHashMap;
public class MapContainer {
private ConcurrentHashMap<ValueContainer, String> map = new ConcurrentHashMap<>();
public void set(ValueContainer key, String value) {
this.map.put(key, value);
}
public String get(ValueContainer key) {
return this.map.get(key);
}
}
At last, there's the ValueContainer.
This class just contains the three Integers x, y and z, and a Constructer to set these values.
package main;
public class ValueContainer {
public ValueContainer(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
public int x, y, z;
}
So when i run the main class, i create a new ValueContainer with the values 1, 2, 3 and put it into the map Container, along with the String "Just a String".
Then i read the String with that exact Value container and print it out.
Sure enough the program works and i get "Just a String" printed in the Console.
So now there's my game:
In my game i have to access a similar ConcurrentHashMap, but i cant use the same ValueContainer to access the String, but i have to create a new one with new ValueContainer(1, 2, 3);
So obviously the ConcurrentHashMap can't give "Just a String" back, because it's not the same ValueContainer, so it gives NULL.
Here's the code of the Main class with this little modification:
package main;
public class Main {
private MapContainer con = new MapContainer();
public static void main(String[] args) {
new Main();
}
public Main() {
this.con.set(new ValueContainer(1, 2, 3), "Just a String");
System.out.println(this.con.get(new ValueContainer(1, 2, 3)));
}
}
Now my question:
Is there any way for me to use the version in the second main class, but without the issue, so that i
get printed out "Just a String" in Console?
Thank you.

Yes, quite simple.
You have to override the two methods Object.hashCode() and Object.equals() in your class ValueContainer.
Please take a look add the API-documentation of the two methods.
API
Maybe you use a IDE like Ecplise oder IntelliJ which will help you with the details.

Related

Driver class for Array

How would I develop the driver class for this code ive written ?
Array Class:
import java.util.Scanner;
public class Array
{
Scanner sc = new Scanner(System.in);
private double[] array = new double[];
public void setArray(double[] arr)
{
//I must set a value for the array length. set by user.
//user must input data
}
public boolean isInIncreasingOrder()
{
//must test if input is in increasing order
}
public boolean isInDecreasingOrder()
{
//must test if input is in descending order
}
public double getTotal()
{
//must find the total of all input
//total +=total
}
public double getAverage()
{
//must calculate average
//average = total/array.length
}
}
I guess what I'm asking is what exactly do i call in the DriverClass and how do I do it.
Thanks
The simplest way to test a class is to have a "public static void main(String[] args)" method in the class itself.
In this "main" method, you first create an instance of the class, and then call the various methods in the class, and verify that they do what you expect. To make testing easier, you might want to print out a message after each call to the class under test, showing the expected result, the actual result, and a friendly "OK" or "FAIL" to let you see easily if the method did what you wanted.
Example:
class MyClass {
private int x = 0;
public int getX() { return x;}
public void setX(int x) { this.x = x; }
public static void main(String[] args) {
MyClass instance = new MyClass();
instance.setX(42);
int value = instance.getX();
System.out.print("Expected 42, got "+value);
if (value == 42) {
System.out.println("OK");
}
else {
System.out.println("FAIL");
}
}
}
Once you're familiar with this approach to testing, you might look into unit test frameworks such as JUnit, which provide better ways to "assert" that a particular test is passing, and to understand the results of your testing.

Java - use class defned in Main in another class

I'm new to Java and we have to make this little project. So i have 3+ classes.
MY code is in pastebin
http://pastebin.com/GEq9DLiP
etc. etc.
Problem is, that in 3rd class it sais
"kangelane cannot be resolved"
but kangelane is already "defined" in Main.java and they are in same package.
Oh and Eclipse also wants to add "open bracet" after
int sook = 4; or
int dam;
and also "clode bracket" to the end
even though all open brackets are closed and vice versa
I have Getters and Setters in "Voitleja.java", so that works.
It also worked, when i only had 2 classes not 3 (Voitlus was in Main)
The problem is that main is a static class, if you want to use a variable in both main and in an oter class, you have to defined it as a static variable, for your example you should do :
public class Main {
static Voitleja kangelane;
public static void main(String[] args) {
String nimi = JOptionPane.showInputDialog("Sisestage võitleja nimi");
kangelane = new Voitleja(nimi, 55, 12);
}
}
Then in your other class (assuming the import are correct)
public class Voitlus{
Random generator = new Random();
int dam;
int sook = 4;
while (true) {
Main.kangelane.setElud(kangelane.getElud() + 7);
}
}
public static void main(String[] args) {
String nimi = JOptionPane.showInputDialog("Sisestage võitleja nimi");
Voitleja kangelane = new Voitleja(nimi, 55, 12);
}
(later)
while (true) {
kangelane.setElud(kangelane.getElud() + 7);
}
The variables are in a different scope. You have to declare kangelane right after your public class declaration so both methods can "see" it.

Java how and where to initialize variable with NullPointerException

I'm having a problem where I receive this error:
Exception in thread "main" java.lang.NullPointerException
at com.noxia.Main.startCombat(Main.java:101)
at com.noxia.Area1.createArea1Enemy(Area1.java:43)
at com.noxia.Main.main(Main.java:30)
I know that I need to initialize the variables because they are null, but I can't seem to figure out what I need to put where. I've minimized the code to show just the relevant parts as there are many other variables and methods left out, but this seems to pertain to the issue. Any help would be greatly appreciated =)
public class Main {
Player p;
Enemy e;
Area1 a1;
public static void main(String[] args) {
Main main = new Main();
main.a1 = new Area1();
main.p = new Player(100);
//the line directly below this is line 30 where the error occurs
main.a1.createArea1Enemy(10);
}
public void startCombat()
{
//the line directly below this is line 101 where the error occurs
while (p.getCurrentLife() > 0 & a1.e.getLife() > 0)
{
p.playerAttack();
if (p.getCurrentLife() > 0 & a1.e.getLife() > 0)
{
e.enemyAttack();
}
}
}
public class Player extends Main {
private int currentLife;
public int getCurrentLife()
{
return currentLife;
}
public void setCurrentLife(int cl)
{
currentLife = cl;
}
public Player(int cl)
{
currentLife = cl;
}
public class Enemy extends Main {
private int life;
public int getLife()
{
return life;
}
public void setLife(int lf)
{
life = lf;
}
public Enemy (inf lf)
{
life = lf;
}
public class Area1 extends Main {
public void createArea1Enemy(int enemyCounter)
{
while (enemyCounter > 0)
{
String[] enemyList = {"Enemy1", "Enemy2"} //code for Enemy2 left out below
int enemyListLength = enemyList.length;
int randomEnemy = (int) (Math.random() * enemyListLength);
if (enemyList[randomEnemy].equals("Enemy1"))
{
Enemy enemy1 = new Enemy("Enemy1", 100);
//the line directly below this is line 43 where the error occurs
startCombat();
}
enemyCounter--;
}
}
}
The simple answer is that you have to set e to an enemy before calling startCombat.
But a better way to do this would be to remove e, and pass the enemy object to startCombat using a method parameter. The e field is conceptually wrong. To understand the wrongness, try to come up with a coherent explanation of what it means in terms of the state of a Main object.
Clearly this is beginner code ... and there are a number of bad things about what you have written:
The fields of a class should be for object state, not for passing parameter values to methods.
You should avoid writing code that accesses the innards of a class ... like your main method does.
Best practice is to make fields private, and defined getter and / or setter methods (as required) for external classes to access / modify them.
You need to learn how to write constructors with parameters.
You need to design your code properly. Everything extending Main means that there is going to be no rational model of what the objects "mean". And there's the problem that each instance of Enemy and Area1 will have their own copies of the p, e, and a1 fields, and a whole bunch of inappropriate methods.
The main problem is that you never initialize Enemy e;. You create an enemy but never assign it to this.e.
Change this line:
Enemy enemy1 = new Enemy("Enemy1", 100);
To this:
this.e = new Enemy("Enemy1", 100);
There are also many other problems with your code.
Learn how to write a constructor properly. This code is wrong.
I see no reason at all why a Play, Area1, and Enemy should extend Main.

How can I use the Command Pattern in Java like I do in C++?

I was working on a game recently in C++ where I implemented the Command Pattern to manage keyboard input to control a spaceship. When instantiating all the commands, I'd pass the spaceship pointer to each constructor so all commands were working with the same spaceship object.
This pattern made sense in C++ because you can pass by reference, but in Java everything is pass-by-value. If I tried to implement the same thing in Java, how would I have each command pointing to the same object?
In all the examples I've seen of the Command Pattern used in Java it makes no difference whether the member variables of each Command are copies or references.
Java does pass by value in all cases, however all 'non-primitive' types exist as references, so for types like String, List or Spaceship you're actually passing a reference around - the pass by value is because you're passing the reference by value. Which is a pretty confusing way of expressing it, I always felt.
Anyway, the upshot of it is that if you have a (hugely simplified) Command class
public class Command {
private Spaceship spaceship;
public Command(Spaceship ship) {
spaceship = ship;
}
}
And then you do this:
Spaceship ship = new Spaceship();
List<Command> ships = new List<Command>();
for (int i = 0; i < 40; i++) {
ships.add(new Command(ship));
}
then every single one of those Command objects has a reference to the same Spaceship object. Passing the reference by value does not cause a copy of the Spaceship object that the reference points to. However, if you were passing ints around, you would indeed be passing copies.
Just remember the distinction between primitive types like int, and object types like String and you'll be fine. It's helpful to remember that primitive type names begin with a lowercase letter (and can never be user-defined), while object types begin with a capital letter. All user-defined types are object types.
Thanks for your help guys. At the end of the day it came down to my complete misunderstanding about how Java works. I was under the impression (for some strange reason) that creating a Command and giving it my object meant that it received a copy instead of a reference to the original. If that was the case then calling .execute() in a Command would have had no effect on the object outside of the class.
Yet, I found that this was not the case after creating a small test:
Sprite.java:
public class Sprite {
private int x;
Sprite(int x) {
this.x = x;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
}
Command.java:
public interface Command {
void execute();
}
MoveLeftCommand.java:
public class MoveLeftCommand implements Command {
private Sprite s;
MoveLeftCommand(Sprite s) {
this.s = s;
}
public void execute() {
s.setX(s.getX() - 1);
}
}
MoveRightCommand.java:
public class MoveRightCommand implements Command {
private Sprite s;
MoveRightCommand(Sprite s) {
this.s = s;
}
public void execute() {
s.setX(s.getX() + 1);
}
}
Test.java:
import java.lang.*;
import java.util.*;
public class Test
{
public static void main(String[] args) {
Sprite mario = new Sprite(0);
Command command = null;
Map<String, Command> commands = new HashMap<String, Command>();
commands.put("a", new MoveLeftCommand(mario));
commands.put("d", new MoveRightCommand(mario));
// Test...
System.out.println(mario.getX()); // 0
command = (Command) commands.get("a");
command.execute();
System.out.println(mario.getX()); // -1
command.execute();
System.out.println(mario.getX()); // -2
command = (Command) commands.get("d");
command.execute();
System.out.println(mario.getX()); // -1
}
}
I correctly saw 0 -1 -2 -1 in the console, just as I would have in C++.
In java they passed "by value"... You pass real object that will implement an execute method. Sharing reference among different objects can be achieved by proper OO design or lookup method injection for common attribute. The Command pattern example was taken from wikipedia
/*the Command interface*/
public interface Command {
void execute();
}
/*the Invoker class*/
import java.util.List;
import java.util.ArrayList;
public class Switch {
private List<Command> history = new ArrayList<Command>();
public Switch() {
}
public void storeAndExecute(Command cmd) {
this.history.add(cmd); // optional
cmd.execute();
}
}
/*the Receiver class*/
public class Light {
public Light() {
}
public void turnOn() {
System.out.println("The light is on");
}
public void turnOff() {
System.out.println("The light is off");
}
}
/*the Command for turning on the light - ConcreteCommand #1*/
public class FlipUpCommand implements Command {
private Light theLight;
public FlipUpCommand(Light light) {
this.theLight = light;
}
public void execute(){
theLight.turnOn();
}
}
}
Since Java always does pass by value, you will need to do something mutable to pass through instead (pass a mutable object).
Consider this example:
In this agnostic language that supports pass-by-reference
string commandArg = "";
new StringCommand().execute(commandArg);
Console.write(commandArg);
And in StringCommand.execute(string arg), you have arg += "Hello World", then your output will be Hello World.
In Java, simply pass a StringBuilder as follows:
StringBuilder sb = new StringBuilder();
new StringCommand().execute(sb);
System.out.println(sb.toString());
where StringCommand.execute(StringBuilder sb) contains:
sb.append("Hello World");
The StringBuilder reference is passed by value in StringCommand.execute. In a nutshell, primitive types are passed by value and, for objects, the reference of the object is passed by value.
I hope this helps.

how to stop getting the same number when generating 2 numbers from a different class

When I run this code i get 2 numbers (which is good) but the numbers generated are the same (which is bad) and I dont want the numbers to be the same. I've done this as an experiment for a rpg I was going to make so I thought it would be beter if each weapon had a different class.
The main class:
package battlesimMK2;
public class Main {
public static void main(String Arg[]) {
String WeponEquiped = "BasicAxe";
System.out.print(BasicAxe.Str);
System.out.print(BasicAxe.Str);
}
}
The basic axe class:
package battlesimMK2;
import java.util.Random;
public class BasicAxe {
static Random rnd = new Random();
static int Str = rnd.nextInt(4)+5;
}
This line:
static int Str = rnd.nextInt(4)+5;
declares a static variable and initializes it once. If you want the code to run each to you access Str, you should make it a method:
public static int getStrength() {
return rnd.nextInt(4)+5;
}
Then call it with this code in Main.main:
System.out.print(BasicAxe.getStrength());
System.out.print(BasicAxe.getStrength());
An alternative which would probably be more object-oriented would be to make the strength an instance field, so that each axe created had a possibly-different (but persistent) strength:
public class BasicAxe {
private static final Random rnd = new Random();
private final int strength;
public BasicAxe() {
strength = rnd.nextInt(4)+5;
}
public int getStrength() {
return strength;
}
}
Then in Main.main:
BasicAxe axe1 = new BasicAxe();
BasicAxe axe2 = new BasicAxe();
System.out.println(axe1.getStrength());
System.out.println(axe2.getStrength());
System.out.println(axe1.getStrength());
Here, the first and third lines of output will be the same - but the second will (probably) be different.
You're generating a single random number and printing it twice. Try something like this instead:
package battlesimMK2;
public class Main {
public static void main(String Arg[]) {
String WeponEquiped = "BasicAxe";
System.out.print(BasicAxe.Str());
System.out.print(BasicAxe.Str());
}
}
package battlesimMK2;
import java.util.Random;
public class BasicAxe {
static Random rnd = new Random();
static int Str() { return rnd.nextInt(4)+5; }
}
This because this line
static int Str = rnd.nextInt(4)+5;
runs just one time in whole the lifecycle of your application. It's static value, you should use static method instead.
Because you define the Str variable as static, only a single copy of that variable is shared between all your BasicAxe classes.
The way to get a different answer each time you ask for the int value is, to use the example posted by the previous poster,
String WeponEquiped = "BasicAxe";
System.out.print(BasicAxe.getStrength());
System.out.print(BasicAxe.getStrength());
But, if you want to create an actual instance of the class BasicAxe, which keeps it's value so that each time you ask for the strength you get the same value, you'll need something different.

Categories