hashmap custom class key && object saving/loading - java

Been working on a project for a while now and I've come across a few different complications and solutions that don't seem to pan out together.
final public class place implements Serializable {
private static final long serialVersionUID = -8851896330953573877L;
String world;
Double X;
Double Y;
Double Z;
}
HashMap<place, Long> blockmap = new HashMap<place, Long>(); // does not work
HashMap<Location, Long> blockmap = new HashMap<Location, Long>(); //works
First, my hashmap is a hashmap containing the time an item was placed (or added) to the world. place is a 'class place {}' containing String world, double x, double y, double z; The problem i've had with this, is that it doesn't work with hashmaps. I can store a new hash key using it, but i cant call to get its value. Using Location instead fixes this problem (hashmap) and works flawlessly.
public void SetBlock(Block block) {
Location loc = new Location(null, block.getLocation().getX(),block.getLocation().getY(),block.getLocation().getZ());
//...
Long time = (long) (System.currentTimeMillis() / 60000);
//...
if (blockmap.containsKey(loc)) {
blockmap.remove(loc);
blockmap.put(loc, time);
//System.out.println("MyLeveler: Block Existed, Updated");
} else {
blockmap.put(loc, time);
//System.out.println("MyLeveler: Block added to " + loc.getX() + ", " + loc.getY() + ", " + loc.getZ());
//System.out.println("MyLeveler: total blocks saved: " + blockmap.size());
}
}
This works without error. Now, for the purpose, this data has to be saved and reloaded when the plugin is disabled, and enabled. To do this, i created a new java class file with a save/load feature.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SLAPI {
public static void save(Object obj,String path) throws Exception
{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
oos.writeObject(obj);
oos.flush();
oos.close();
}
public static Object load(String path) throws Exception
{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
Object result = ois.readObject();
ois.close();
return result;
}
}
I typically get "notserializable" errors. Using 'implements Serializable' and ois.defaultReadObject() or oos.defaultWriteObject() which checks the serial on the file results in a clean save/load only when the object is EMPTY! When it contains data, i constantly get "java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException"
This is clearly a problem! One of the recommendations here: ArrayList custom class as HashMap key failed to produce any better results. In fact, creating a custom class was my first issue to begin with >.>
So i guess the questions are:
1) What would i have to alter to use the custom class as a key (and work properly)
2) Why doesn't it recognize that i'm setting it as a serializable class/function/java class
3) Why does it work with an empty hashmap, but not with a filled hashmap?

Basically you need to override hashCode() and equals() in place. Presumably Location already overrides these methods.
Those are the methods that HashMap uses to first narrow down the list of candidate keys very quickly (using the hash code) and then check them for equality (by calling equals).
It's not clear what the serializable problem is - my guess is that although place is serializable, Location isn't. If you could post a short but complete problem demonstrating the problem, that would really help. (It would also be a good idea to start following Java naming conventions, and making your fields private...)
EDIT: Here's an example of the Place class with hash code and equality. Note that I've made it immutable for the sake of avoiding the values changing after it's used as a key in a hash map - I don't know offhand how well that works with serialization, but hopefully it's okay:
public final class Place implements Serializable {
private static final long serialVersionUID = -8851896330953573877L;
private final String world;
// Do you definitely want Double here rather than double?
private final Double x;
private final Double y;
private final Double z;
public Place(String world, Double x, Double y, Double z) {
this.world = world;
this.x = x;
this.y = y;
this.z = z;
}
#Override public int hashCode() {
int hash = 17;
hash = hash * 31 + (world == null ? 0 : world.hashCode());
hash = hash * 31 + (x == null ? 0 : x.hashCode());
hash = hash * 31 + (y == null ? 0 : y.hashCode());
hash = hash * 31 + (z == null ? 0 : z.hashCode());
return hash;
}
#Override public boolean equals(Object other) {
if (!(other instanceof Place)) {
return false;
}
Place p = (Place) other;
// Consider using Guava's "Objects" class to make this simpler
return equalsHelper(world, p.world) &&
equalsHelper(x, p.x) &&
equalsHelper(y, p.y) &&
equalsHelper(z, p.z);
}
private static boolean equalsHelper(Object a, Object b) {
if (a == b) {
return true;
}
if (a == null || b == null) {
return false;
}
return a.equals(b);
}
// TODO: Add getters?
}
It's worth noting that this will be comparing Double values for equality, which is almost always a bad idea... but you can't really give a tolerance in something like equals. So long as the values are exactly the same when you come to look them up, it should work fine.

Related

How to avoid having multiple parameter for a function

I'm developing a game in which there're many classes. The game appears like a grid plane. I have a function which can detect whether a grid consist of any kind of specified class of object. This function return true if the grid contain any one of the specified type of object and return false if there's none.
However, when the number of classes needed to be detected increase, the parameter list can easily become awfully long, does anyone know how can I resolve that problem? Any design pattern would help? Or my design is acceptable in this case?
public boolean sameClass(int x, int y, String... className) {
for (Entity entity : entities) {
if (entity.getX() == x && entity.getY() == y) {
for (String name : className) {
if (name == entity.getClassName()) {
return true;
}
}
}
}
return false;
}
examples of using the method
sameClass(x, y - 1, "Boulder", "Enemy", "Wall")
sameClass(x, y - 1, "Player")
You can send Collection to your method:
Set<String> params = new HashSet("Boulder", "Enemy", "Wall");
boolean result = sameClass(x, y - 1, params);
You can use Builder-like pattern:
boolean result = new Checker(x, y - 1)
.param("Boulder")
.param("Enemy")
.param("Wall")
.check();
Also, if "Boulder", "Enemy", "Wall" are class of unit, it's better to use Enum instead of strings.
=== Example of possible solution ===
public class Checker {
private int x;
private int y;
private Set<Type> params = new HashSet();
// Entity related code here
public Checker(int x, int y) {
this.x = x;
this.y = y;
}
public Checker param(Type type) {
this.params.add(type);
return this;
}
public boolean check() {
for (Entity entity : entities) {
if (entity.getX() == x && entity.getY() == y) {
return params.contains(entity.getType());
}
}
return false;
}
public enum Type {
BOULDER,
ENEMY,
WALL,
PLAYER
}
}
First of all, don't ever try to compare java strings for equality using '==' unless otherwise you are testing for reference equality only. Rather use .equals() method. Read How do I compare strings in Java? to know more on this aspect.
And, for your actual problem, you can use different techniques. I would prefer to send array of Strings as parameter to keep the method call simple.
Implement your method like below:
public boolean sameClass(int x, int y, String[] className) {
for (Entity entity : entities) {
if (entity.getX() == x && entity.getY() == y) {
for (String name : className) {
if (name.equals(entity.getClassName())) {
return true;
}
}
}
}
return false;
}
Then create a class to store all the possible class name check combination you want to check for:
public class ClassNameCollection {
public static final String[] detectMultiple = new String[]{ "Boulder", "Enemy", "Wall" };
public static final String[] detectPlayer = new String[]{ "Player" };
}
When using this method, try something like below:
sameClass(x, y - 1, ClassNameCollection.detectMultiple);
sameClass(x, y - 1, ClassNameCollection.detectPlayer);
This is actually similar to the way you are handling it using var..args but one advantage of using this way I have described is, for a particular purpose (in your case- detecting wall, detecting equipable , etc.) you can create array of strings once and can call the method using that array variable multiple number of times without the need of writing those large number of lists of class names several times.

How to re-use value in different functions?

I am programming a Study in MotiveWave, a program used for (day)trading. The study is its own class. (info about MotiveWave's SDK found here: https://www.motivewave.com/sdk/javadoc/overview-summary.html)
public class L_V2 extends com.motivewave.platform.sdk.study.Study
My study uses 2 different timeframes: the 1 hour and the 4 hour bars. Both are calculated in a different function. Otherwise formulated: both use a different dataseries, as shown in the code below.
I have two values, being calculated on the 4 hour timeframe, called 'ma9' and 'ma11' that I would like to use in an 'if'-statement on the 1 hour timeframe.
This is the code for the 4 hour timeframe. It simply calculates 2 moving averages
#Override
protected void calculateValues(DataContext ctx)
{
int maPeriodTF2 = getSettings().getInteger(MA_PERIOD_TF2);
int ma2PeriodTF2 = getSettings().getInteger(MA2_PERIOD_TF2);
//Object maInput = getSettings().getInput(MA_INPUT, Enums.BarInput.CLOSE);
BarSize barSizeTF2 = getSettings().getBarSize(MA_BARSIZE_TF2);
DataSeries series2 = ctx.getDataSeries(barSizeTF2);
StudyHeader header = getHeader();
boolean updates = getSettings().isBarUpdates() || (header != null && header.requiresBarUpdates());
// Calculate Moving Average for the Secondary Data Series
for(int i = 1; i < series2.size(); i++) {
if (series2.isComplete(i)) continue;
if (!updates && !series2.isBarComplete(i)) continue;
// MA TF2
Double ma9 = series2.ma(getSettings().getMAMethod(MA_METHOD_TF2), i, maPeriodTF2, getSettings().getInput(MA_INPUT_TF2));
Double ma11 = series2.ma(getSettings().getMAMethod(MA2_METHOD_TF2), i, ma2PeriodTF2, getSettings().getInput(MA2_INPUT_TF2));
series2.setDouble(i, Values.MA9_H4, ma9);
series2.setDouble(i, Values.MA11_H4, ma11);
}
// Invoke the parent method to run the "calculate" method below for the primary (chart) data series
super.calculateValues(ctx);
I would now like to use those 2 values, 'ma9' and 'ma11' in another function, on the 1 hour timeframe:
#Override
protected void calculate(int index, DataContext ctx)
DataSeries series=ctx.getDataSeries();
if (ma9 < ma11 && other conditions)
{ctx.signal(index, Signals.YOU_SHOULD_BUY, "This would be my buying signal", series.getClose(index));
}
How can I export the ma9 and the ma11 so they become 'global' and I can re-use them in this other function ?
Basically, the idea is to store somewhere the values or just pass them appropriately after being computed.
There is a java pattern based on singleton that allow you to store/retrieve values inside a class (using a collection : HashMap). Any values could be added,retried in any classes based on predefined (key,value) using the construction Singelton.getInstance() with HashMap standard operation (put, get).
Maybe this example could be useful.
import java.util.Hashtable;
class Singleton extends Hashtable<String, Object> {
private static final long serialVersionUID = 1L;
private static Singleton one_instance = null;
private Singleton() {
};
public static Singleton getInstance() {
one_instance = (one_instance == null) ? new Singleton() : one_instance;
return one_instance;
}
}
import java.util.Random;
public class Reuse {
public static void main(String[] args) {
Reuse r = new Reuse();
Compute c = r.new Compute();
Singleton.getInstance().put("r1", c.getRandom());
Singleton.getInstance().put("r2", c.getRandom());
Singleton.getInstance().put("n", c.getName());
System.out.println(Singleton.getInstance().get("r1"));//print random_number_1
System.out.println(Singleton.getInstance().get("r2"));//print random_number_2
System.out.println(Singleton.getInstance().get("n"));// print name (value for key n)
}
class Compute
{
public Double getRandom()
{
return new Random().nextDouble();
}
public String getName()
{
return "name";
}
}
}

Java heap space on an array of 1134890 x 1134890

I am trying to manage a big set of data which means I need to create an Array like this:
double[][] myArray = new double[1134890][1134890];
By doing so, I get an exception Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
I tried to run it with the following parameters to increase the memory of my virtual machine:
But I still get the same error...
First, did I get it right with the parameters? And if so, is there any chance I can make my program work?
Because this is a very huge array have you considered the idea to use a sparse matrix?
Basically a sparse matrix is a data structure where you save only data that haven't a default value.
Here is a possible basic implementation of a sparse matrix.
public class SparseMatrix {
private Map<String, Object> map;
private Object default;
public SparseMatrix(Object default) {
this.default = default;
map = new HashMap<String, Object>();
}
public Object get(int x, int y) {
String key = x + "." + y;
String value = map.get(key);
if (value == null) {
return default;
}
return value;
}
public void set(int x, int y, Object value) {
String key = x + "." + y;
if (value.equals(default)) {
map.remove(key);
} else {
map.put(key, value);
}
}
}
Note this is not a real implementation class. Take it only as a skeleton to start your code. For example here it is not possible to assign null to the default value, generics are not used for the definition of SparseMatrix and the key should be not a concatenation of strings.

2 dimensional array changing during serialisation

I'm serialising and deserialising a large two dimensional array of objects. Each object contains instructions to creating a BufferedImage - this is done to get around BufferedImage not being directly serializable itself.
The class being serialised is:
public final class MapTile extends TransientImage
{
private static final long serialVersionUID = 0;
private transient BufferedImage f;
transient BufferedImage b;
int along;
int down;
boolean flip = false;
int rot = 0;
public MapTile(World w, int a, int d)
{
// f = w.getMapTiles();
along = a;
down = d;
assignImage();
}
public MapTile(World w, int a, int d, int r, boolean fl)
{
// f = w.getMapTiles();
along = a;
down = d;
rot = r;
flip = fl;
assignImage();
}
public int getA()
{
return along;
}
public int getD()
{
return down;
}
#Override
public void assignImage()
{
if (f == null)
{
f = World.mapTiles;
}
b = f.getSubimage(along, down, World.squareSize, World.squareSize);
if (rot != 0)
{
b = SmallMap.rotateImage(b, rot);
}
if (flip)
{
b = SmallMap.flipImage(b);
}
super.setImage(b);
f.flush();
b.flush();
f = null;
b = null;
}
}
which extends:
public abstract class TransientImage implements Serializable
{
private transient BufferedImage image;
public BufferedImage getImage()
{
return image;
}
public void setImage(BufferedImage i)
{
image = i;
}
public abstract void assignImage();
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
in.defaultReadObject();
assignImage();
}
}
This will ultimately be part of a map - usually it is created randomly but certain areas must be the same each time, hence serialising them and reading the array back in. As I will never need to save the image during normal usage I am putting in the write code:
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("verticalroad.necro")))
{
//out.writeObject(mapArray);
//}
//catch (IOException e) {
//}
in the class that creates the map, the read code:
try{
FileInputStream door = new FileInputStream(new File(f.getPath()+ "//verticalroad.necro"));
ObjectInputStream reader = new ObjectInputStream(door);
homeTiles = (MapTile[][]) reader.readObject();
}
catch (IOException | ClassNotFoundException e)
{
System.out.println("Thrown an error" + e.getMessage());
}
in the initialising class and commenting in and out as needed.
However. Each time I run the program the contents of the two dimensional array (mapArray in write, homeTiles in read) is different. Not only different from the one I (thought) I wrote, but also different each time the program is opened.
As can be seen, I'm printing out the toString to System.out which reveals further oddities. As its just a standard array, the toString isn't 100% helpful but it seems to cycle between several distinct values. However, even when the toStringg gives the same value, the contents of the array as displayed are not the same.
An example of a toString is hometiles:[[Lriseofthenecromancer.MapTile;#7681720a Looking at the documentation for Array.toString (here) it seems to be badly formed, lacking a trailing ]. I'm not sure if this is a clue to the issue or if its simply that the array is very large (several thousand objects) and its an issue of display space (I'm using NetBeans).
Any insight as to why this is changing would be appreciated. My working assumption is that its serializing the array but not the contents. But I have no idea a) if that's the case and b)if it is, what to do about it.
EDIT: Looking into this a bit further, it seems that instance variables aren't being set immediately. Printing them out directly after the call to setImage() has them all at zero, printing them from the calling class has them where they should be.
The underlying problem was that I'm an idiot. The specific expression of this in this particular case was that I forgot that subclasses couldn't inherit private methods. As such, the assignImage call wasn't being made and the image wasn't being set up.
Sorry for wasting the time of anyone who looked at this. I feel quite embarrassed.

Java MultiMap Not Recognizing Key

I'm trying to store multiple values for a key in a data structure so I'm using Guava (Google Collection)'s MultiMap.
Multimap<double[], double[]> destinations = HashMultimap.create();
destinations = ArrayListMultimap.create();
double[] startingPoint = new double[] {1.0, 2.0};
double[] end = new double[] {3.0, 4.0};
destinations.put(startingPoint, end);
System.out.println(destinations.containsKey(startingPoint));
and it returns false.
Note: Key-values are being stored in the multimap as the destinations.size() increases when I put something there.It also does not happen when keys are String instead of double[].
Any idea what the problem is?
Edit: Many thanks to Jon Skeet I now implemented the class:
class Point {
double lat;
double lng;
public boolean equals(Point p) {
if (lat == p.lat && lng == p.lng)
return true;
else
return false;
}
#Override
public int hashCode() {
int hash = 29;
hash = hash*41 + (int)(lat * 100000);
hash = hash*41 + (int)(lng * 100000);
return hash;
}
public Point(double newlat, double newlng) {
lat = newlat;
lng = newlng;
}
}
And now I have a new problem. This is how I'm using it:
Multimap<Point, Point> destinations = HashMultimap.create();
destinations = ArrayListMultimap.create();
Point startingPoint = new Point(1.0, 2.0);
Point end = new Point(3.0, 4.0);
destinations.put(startingPoint, end);
System.out.println( destinations.containsKey(startingPoint) );
System.out.println( destinations.containsKey(new Point(1.0, 2.0)) );
The first one returns true, the second one returns false. It gives me an error if I put #Override before the equals method.Any Idea what the problem is now?
Thanks :)
Edit2: It now behaves exactly as expected when I changed equals to this:
#Override
public boolean equals(Object p) {
if (this == p)
return true;
else if ( !(p instanceof Point) )
return false;
else {
Point that = (Point) p;
return (that.lat == lat) && (that.lng == lng);
}
}
Thanks everyone.
You're using arrays as the hash keys. That's not going to work - Java doesn't override hashCode and equals for arrays. (The Arrays class provides methods to do this, but it's not going to help you here.) Admittedly I'd expect it to work in this specific case, where you're using the exact same reference for both put and containsKey... When I test your code, it prints true. Are you sure you can reproduce it with exactly your code?
For example, while I'd expect it to work for the code you've given, I wouldn't expect this to work:
// Logically equal array, but distinct objects
double[] key = (double[]) startingPoint.clone();
System.out.println(destinations.containsKey(key));
It sounds like you shouldn't really be using double[] here - you should create a Point class which has two double variables, and overrides equals and hashCode.
Additionally, using double values in hash keys is usually a bad idea anyway, due to the nature of binary floating point arithmetic. That's going to be a problem even using the Point idea above... it should be okay if you don't need to actually do any arithmetic (if you're just copying values around) but take great care...
The problem is that you cannot hash "equal" arrays and get the same result each time. For example:
public static void main(String[] args) {
System.out.println(new double[]{1.0, 2.0}.hashCode());
System.out.println(new double[]{1.0, 2.0}.hashCode());
}
will result something like
306344348
1211154977

Categories