Related
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.
Is there a way in Java to create a collection (map) with fixed size and length?
I.e., I would like to initialize it with K constant keys (e.g. strings) but still want to be able to change the values.
Edit:
The test case has a fixed number of objects, each one corresponds to a number (float). Each time a specific event in the application occurs, I would like to multiply all the numbers in the collection, except the number that corresponds to the object that "caused" the event.
The number is not logically an attribiute of the object.
I suggest you first look at Mike's answer to get an idea of how to go about solving this problem, then make some changes to the code he provided so it will work in your situation:
import java.util.HashMap;
public class InstrumentedHashMap<K> extends HashMap<K, Float> {
private static final long serialVersionUID = 1L;
private int MAX;
public InstrumentedHashMap(int capacity) {
super();
MAX = capacity;
}
#Override
public Float put(K key, Float value) {
if (super.size() >= MAX && !super.containsKey(key)) {
return null;
} else {
super.put(key, value);
return value;
}
}
public void event(K trigger, int multiplyAmount, float subtractAmount) {
super.entrySet().stream().forEach(e -> {
if (!e.getKey().equals(trigger))
e.setValue(e.getValue() * multiplyAmount);
else
e.setValue(e.getValue() - subtractAmount);
});
}
}
You can use the InstrumentedHashMap#event method to handle your "specific event", with the multiplyAmount parameter being the value that you want to multiply your floats by.
I got a problem where I am not allowed to use switch/case or if/else queries.
I got a config file I read which is this:
650;0;1.5;month
614;0;2.88;year
466;0;2.48;week
716;0;4.6;half-year
718;0;2.6;quarter
I am splitting those Strings at the ";", so it is saved in an array. The problem I have, that I need to do other things in the code for each time given in that array ar[3], so if it is a month I need other calculations then when it is a full year.
But I am not allowed to do this with Switch/case or If/Else, now I am getting confused.
If (ar[3] = month){
do this;
else if (ar[3] = year) {
do this;
}
How am I doing this object oriented? Thanks for every help :)
Polymorphism by Inheritance is your friend
It seems like you need some sort of inheritance structure based on the time period in ar[3]. The special do this method could be coded for each case. That way you get the ability to do something different for each case. You just need a way to instantiate the correct subtype in the first place. There are a number of ways you could approach this.
The Conditional Operator
The most direct approach IMHO is the conditional operator, ?:.
So the code would look something like this:
MyClass x = ar[3].equals("month") ? new MyClassMonth() :
(ar[3].equals("year") ? new MyClassYear() :
(ar[3].equals("week") ? new MyClassWeek() :
(ar[3].equals("half-year") ? new MyClassHalfyear() :
new MyClassQuarter())));
x.doSomething();
The nested conditional expressions give you the ability to select the right class, and the inheritance gives you the polymorphic behavior you want.
But you mentioned in comment that you also can't use ?:. What next?
A Map of Stateless Objects
Suppose you wrote MyClassMonth in a way that nothing in it depended on any remembered state, i.e. the doSomething() method has no side effects. Then you could create a Map<String, MyClass> to store one instance of each subclass, then pull the relevant one out of the map when you needed to invoke.
You'd initialize the map like this:
final Map<String, MyClass> themap = new HashMap<>();
{
themap.add("month", new MyClassMonth());
themap.add("year", new MyClassYear());
themap.add("week", new MyClassWeek());
themap.add("half-year", new MyClassHalfyear());
themap.add("quarter", new MyClassQuarter());
}
And invoke doSomething() with ar as argument:
MyClass x = themap.get(ar[3]);
if (x != null)
x.doSomething(ar);
Other Options
There are other ways to do this. Sticking with the Map concept, you could store class literals in the Map instead of instances, then instantiate them reflectively. You could also keep a lambda in the Map and invoke it.
Enums
#OldCurmudgeon suggested using enums. If you put those enums into the Map and add a lambda to the enum, you can grab the enum and invoke the lambda. That would work and has a certain appeal, but it seems unnecessary. You'd be better off just invoking the lambda directly.
You could use an enum as a command factory pattern and implement the choice with a Map lookup.
// Lookups for teh period.
static final Map<String, Period> lookup = new HashMap<>();
enum Period {
Month("month") {
#Override
void process(int x, int y, double v) {
// Processing for "month" records here.
System.out.println(this + "-process(" + x + "," + y + "," + v + ")");
}
},
Year("year") {
#Override
void process(int x, int y, double v) {
// Processing for "year" records here.
System.out.println(this + "-process(" + x + "," + y + "," + v + ")");
}
},
Quarter("quarter") {
#Override
void process(int x, int y, double v) {
// Processing for "quarter" records here.
System.out.println(this + "-process(" + x + "," + y + "," + v + ")");
}
},
HalfYear("half-year") {
#Override
void process(int x, int y, double v) {
// Processing for "half-year" records here.
System.out.println(this + "-process(" + x + "," + y + "," + v + ")");
}
};
Period(String inData) {
// Record me in the map.
lookup.put(inData, this);
}
abstract void process(int x, int y, double v);
static void process(String data) {
String[] parts = data.split(";");
Period p = lookup.get(parts[3]);
if (p != null) {
p.process(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]), Double.parseDouble(parts[2]));
}
}
}
public void test() {
String[] test = {"650;0;1.5;month",
"614;0;2.88;year",
"466;0;2.48;week",
"716;0;4.6;half-year",
"718;0;2.6;quarter",};
for (String s : test) {
Period.process(s);
}
}
correctly prints:
Month-process(650,0,1.5)
Year-process(614,0,2.88)
HalfYear-process(716,0,4.6)
Quarter-process(718,0,2.6)
Note that there is one if in there but that is only defensive to avoid bad data - it is not part of the lookup mechanism.
Something like this:
public interface Calculator {
double calculate(int p1, int p2, double p3);
}
public class YearCalculator implements Calculator {
public double calculate(int p1, int p2, double p3) {
double value = 0.0;
// do year calculations
return value;
}
}
public class CalculatorFactory {
public Calculator getInstance(String type) {
Calculator calculator = null;
if (type != null) {
} else {
throw new IllegalArgumentException("calculator type cannot be null");
if ("year".equalsIgnoreCase(type)) {
} else {
System.out.println(String.format("No such type: %s", type));
}
}
return calculator;
}
}
You have to have if/else logic in the factory, but not when you're parsing the text.
Your processing code:
CalculatorFactory factory = new CalculatorFactory();
// contents is a List of Strings from your input file.
for (String line : contents) {
String [] tokens = line.split(";");
Calculator calculator = factory.getInstance(tokens[3]);
double value = calculator.calculate(Integer.parseInt(tokens[0]), Integer.parseInt(tokens[1]), Double.parseDouble(tokens[2]));
}
Building upon the suggestion given by Codebender as an alternative solution:
You need 5 classes, one for each case, with a common interface but different implementations.
Your interface may look something like this:
public interface MyCalculator {
public double calculate(double a, double b, double c);
}
Then you will need to implement your 5 classes similar to this. You will need a different class with a different implementation for calculate for month, year, week, half-year and quarter:
public class MyMonthCalculator implements MyCalculator {
#Override
public double calculate(double a, double b, double c) {
// Do your calculations here then return
}
}
Then, before your parsing logic, you can add the five classes to a Map.
map.put("month", new MyMonthCalculator());
// Repeat for year, week, half-year and quarter
To actually perform a calculation:
double result = map.get(ar[3]).calculate(Double.parseDouble(ar[0]), Double.parseDouble(ar[1]), Double.parseDouble(ar[2]));
You can simulate if or case with arrays of options. Only problem here would be finding index of our element in such array. We can't use if and case but I assume that while is an option.
So your code can be similar to something like:
String[] options = { "foo", "bar", "baz" };
Runnable[] action = { new Runnable() {
#Override
public void run() {
System.out.println("handling foo");
}
}, new Runnable() {
#Override
public void run() {
System.out.println("handling bar");
}
}, new Runnable() {
#Override
public void run() {
System.out.println("handling baz");
}
} };
String choice = "bar";
int matched = 0;
int i = -1;
while (matched != 1) {
i++;
matched = boolToInt(options[i].equals(choice));
}
action[i].run();
I used method like this to convert boolean to integer where 1=true, 0=false
public static int boolToInt(Boolean b) {
return 5 - b.toString().length();
}
Instead Runnable you can provide your own interface.
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.
I am looking for a pre-built Java data structure with the following characteristics:
It should look something like an ArrayList but should allow indexing via double-precision rather than integers. Note that this means that it's likely that you'll see indicies that don't line up with the original data points (i.e., asking for the value that corresponds to key "1.5"). EDIT: For clarity, based on the comments, I'm not looking to change the ArrayList implementation. I'm looking for a similar interface and developer experience.
As a consequence, the value returned will likely be interpolated. For example, if the key is 1.5, the value returned could be the average of the value at key 1.0 and the value at key 2.0.
The keys will be sorted but the values are not ensured to be monotonically increasing. In fact, there's no assurance that the first derivative of the values will be continuous (making it a poor fit for certain types of splines).
Freely available code only, please.
For clarity, I know how to write such a thing. In fact, we already have an implementation of this and some related data structures in legacy code that I want to replace due to some performance and coding issues.
What I'm trying to avoid is spending a lot of time rolling my own solution when there might already be such a thing in the JDK, Apache Commons or another standard library. Frankly, that's exactly the approach that got this legacy code into the situation that it's in right now....
Is there such a thing out there in a freely available library?
Allowing double values as indices is a pretty large change from what ArrayList does.
The reason for this is that an array or list with double as indices would almost by definition be a sparse array, which means it has no value (or depending on your definition: a fixed, known value) for almost all possible indices and only a finite number of indices have an explicit value set.
There is no prebuilt class in Java SE that supports all that.
Personally I'd implement such a data structure as a skip-list (or similar fast-searching data structure) of (index, value) tuples with appropriate interpolation.
Edit: Actually there's a pretty good match for the back-end storage (i.e. everything except for the interpolation): Simply use a NavigableMap such as a TreeMap to store the mapping from index to value.
With that you can easily use ceilingEntry() and (if necessary) higherEntry() to get the closest value(s) to the index you need and then interpolate from those.
If your current implementation has complexity O(log N) for interpolating a value, the implementation I just made up may be for you:
package so2675929;
import java.util.Arrays;
public abstract class AbstractInterpolator {
private double[] keys;
private double[] values;
private int size;
public AbstractInterpolator(int initialCapacity) {
keys = new double[initialCapacity];
values = new double[initialCapacity];
}
public final void put(double key, double value) {
int index = indexOf(key);
if (index >= 0) {
values[index] = value;
} else {
if (size == keys.length) {
keys = Arrays.copyOf(keys, size + 32);
values = Arrays.copyOf(values, size + 32);
}
int insertionPoint = insertionPointFromIndex(index);
System.arraycopy(keys, insertionPoint, keys, insertionPoint + 1, size - insertionPoint);
System.arraycopy(values, insertionPoint, values, insertionPoint + 1, size - insertionPoint);
keys[insertionPoint] = key;
values[insertionPoint] = value;
size++;
}
}
public final boolean containsKey(double key) {
int index = indexOf(key);
return index >= 0;
}
protected final int indexOf(double key) {
return Arrays.binarySearch(keys, 0, size, key);
}
public final int size() {
return size;
}
protected void ensureValidIndex(int index) {
if (!(0 <= index && index < size))
throw new IndexOutOfBoundsException("index=" + index + ", size=" + size);
}
protected final double getKeyAt(int index) {
ensureValidIndex(index);
return keys[index];
}
protected final double getValueAt(int index) {
ensureValidIndex(index);
return values[index];
}
public abstract double get(double key);
protected static int insertionPointFromIndex(int index) {
return -(1 + index);
}
}
The concrete interpolators will only have to implement the get(double) function.
For example:
package so2675929;
public class LinearInterpolator extends AbstractInterpolator {
public LinearInterpolator(int initialCapacity) {
super(initialCapacity);
}
#Override
public double get(double key) {
final double minKey = getKeyAt(0);
final double maxKey = getKeyAt(size() - 1);
if (!(minKey <= key && key <= maxKey))
throw new IndexOutOfBoundsException("key=" + key + ", min=" + minKey + ", max=" + maxKey);
int index = indexOf(key);
if (index >= 0)
return getValueAt(index);
index = insertionPointFromIndex(index);
double lowerKey = getKeyAt(index - 1);
double lowerValue = getValueAt(index - 1);
double higherKey = getKeyAt(index);
double higherValue = getValueAt(index);
double rate = (higherValue - lowerValue) / (higherKey - lowerKey);
return lowerValue + (key - lowerKey) * rate;
}
}
And, finally, a unit test:
package so2675929;
import static org.junit.Assert.*;
import org.junit.Test;
public class LinearInterpolatorTest {
#Test
public void simple() {
LinearInterpolator interp = new LinearInterpolator(2);
interp.put(0.0, 0.0);
interp.put(1.0, 1.0);
assertEquals(0.0, interp.getValueAt(0), 0.0);
assertEquals(1.0, interp.getValueAt(1), 0.0);
assertEquals(0.0, interp.get(0.0), 0.0);
assertEquals(0.1, interp.get(0.1), 0.0);
assertEquals(0.5, interp.get(0.5), 0.0);
assertEquals(0.9, interp.get(0.9), 0.0);
assertEquals(1.0, interp.get(1.0), 0.0);
interp.put(0.5, 0.0);
assertEquals(0.0, interp.getValueAt(0), 0.0);
assertEquals(0.0, interp.getValueAt(1), 0.0);
assertEquals(1.0, interp.getValueAt(2), 0.0);
assertEquals(0.0, interp.get(0.0), 0.0);
assertEquals(0.0, interp.get(0.1), 0.0);
assertEquals(0.0, interp.get(0.5), 0.0);
assertEquals(0.75, interp.get(0.875), 0.0);
assertEquals(1.0, interp.get(1.0), 0.0);
}
#Test
public void largeKeys() {
LinearInterpolator interp = new LinearInterpolator(10);
interp.put(100.0, 30.0);
interp.put(200.0, 40.0);
assertEquals(30.0, interp.get(100.0), 0.0);
assertEquals(35.0, interp.get(150.0), 0.0);
assertEquals(40.0, interp.get(200.0), 0.0);
try {
interp.get(99.0);
fail();
} catch (IndexOutOfBoundsException e) {
assertEquals("key=99.0, min=100.0, max=200.0", e.getMessage());
}
try {
interp.get(201.0);
fail();
} catch (IndexOutOfBoundsException e) {
assertEquals("key=201.0, min=100.0, max=200.0", e.getMessage());
}
}
private static final int N = 10 * 1000 * 1000;
private double measure(int size) {
LinearInterpolator interp = new LinearInterpolator(size);
for (int i = 0; i < size; i++)
interp.put(i, i);
double max = interp.size() - 1;
double sum = 0.0;
for (int i = 0; i < N; i++)
sum += interp.get(max * i / N);
return sum;
}
#Test
public void speed10() {
assertTrue(measure(10) > 0.0);
}
#Test
public void speed10000() {
assertTrue(measure(10000) > 0.0);
}
#Test
public void speed1000000() {
assertTrue(measure(1000000) > 0.0);
}
}
So the functionality seems to work. I only measured speed in some simple cases, and these suggest that scaling will be better than linear.
Update (2010-10-17T23:45+0200): I made some stupid mistakes in checking the key argument in the LinearInterpolator, and my unit tests didn't catch them. Now I extended the tests and fixed the code accordingly.
In the Apache commons-math library, if you implement the UnivariateRealInterpolator and the return value of its interpolate method which is typed UnivariateRealFunction you'll be most of the way there.
The interpolator interface takes two arrays, x[] and y[]. The returned function has a method, value() that takes an x' and returns the interpolated y'.
Where it fails to provide an ArrayList-like experience is in the ability to add more values to the range and domain as if the List is growing.
Additionally, they look to be in need of some additional interpolation functions. There are only 4 implementations in the library for the stable release. As a commenter pointed out, it seems to be missing 'linear' or something even simpler like nearest neighbor. Maybe that's not really interpolation...
That's a huge change from ArrayList.
Same as Joachim's response above, but I'd probably implement this as a binary tree, and when I didn't find something I was looking for, average the value of the next smallest and largest values, which should be quick to traverse to.
Your description that it should be "like an ArrayList" is misleading, since what you've described is a one dimensional interpolator and has essentially nothing in common with an ArrayList. This is why you're getting suggestions for other data structures which IMO are sending you down the wrong path.
I don't know of any available in Java (and couldn't easily find one one google), but I think you should have a look at GSL - GNU Scientific Library which includes a spline interpolator. It may be a bit heavy for what you're looking for since it's a two dimensional interpolator, but it seems like you should be looking for something like this rather than something like an ArrayList.
If you'd like it to "look like an ArrayList" you can always wrap it in a Java class which has access methods similar to the List interface. You won't be able to actually implement the interface though, since the methods are declared to take integer indices.