I have problem with Java's ArrayList. I've created an Object, that contains two attributes, x and y. Now I've loaded some object in my ArrayList. Problem is that I don't know how to find index of some object with x atribute I'm searching. Is there any way to do this?
Assuming something like:
public class Point {
public final int x;
public final int y;
}
And a declaration of:
List<Point> points = ...;
You can use for-each to iterate through all the points and find the one you want:
for (Point p : points) {
if (p.x == targetX) {
process(p);
break; // optional
}
}
Note that this will not give you the index, but it will give you the Point itself, which sometimes is enough. If you really need the index, then you'd want to use indexed for loop, using size() and get(int index) (see BalusC's answer).
See also
Java Language Guide: the for-each loop
java.util.List API
The above solution searches in O(N) for each targetX. If you're doing this often, then you can improve this by declaring class Point implementsComparable<Point>, using x as the primary sorting key for Collections.sort.
Then you can Collections.binarySearch. With a setup time of O(N log N), each query can now be answered in O(log N).
Another option is to use a SortedSet such as a TreeSet, especially if what you have is a Set<Point>, not a List<Point>.
See also
How to sort an array or ArrayList<Point> ASC first by x and then by y?
Java: What is the difference between implementing Comparable and Comparator?
Is this what you looking for?
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
#Override
public boolean equals(Object o) {
return (o instanceof Point && getX() == ((Point) o).getX() && getY() == ((Point) o)
.getY());
}
}
public class TestIndexOf {
public static void main(String[] args){
Point p1 = new Point(10,30);
Point p2 = new Point(20,40);
Point p3 = new Point(50,40);
Point p4 = new Point(60,40);
List<Point> list = new ArrayList<Point>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
System.out.println(list.indexOf(p3));
}
}
If you just want to search on the x property, change the equals method to compare only the x values like:
#Override
public boolean equals(Object o) {
return (o instanceof Point && getX() == ((Point) o).getX());
}
Just iterate over the list and test every element.
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getX() == someValue) { // Or use equals() if it actually returns an Object.
// Found at index i. Break or return if necessary.
}
}
Verbose, yes, but possibly until JDK7 with Closures, there is no other standard way.
I usually just use a map if i want to be able to fetch an object out of a collection based on one specific attribute value. I find that cleaner than having to iterate over lists.
Map<String, Object> map = new HashMap<String, Object>();
map.put(o1.getX(), o1);
map.put(o2.getX(), o2);
now, if i want the object that has an x-value of "foo", all it takes is
Object desiredObject = map.get("foo");
if order is important, consider a LinkedHashMap.
Related
I have an ArrayList containing objects which have 4 parameters (x, y, iD and myType). I want to verify if there are objects in this ArrayList which have particular coordinates, independently of their iD and myType parameters.
I wanted to use Arrays.asList(yourArray).contains(yourValue) but it is when the object has only one parameter.
Here is the whole code:
public class MyObject {
public float x;
public float y;
public int iD;
public String myType;
public MyObject (float x, float y, int iD, String myType)
{
this.myType = myType;
this.iD = iD;
this.x = x;
this.y = y;
}
#Override
public String toString() {
return ("[iD="+iD+" x="+x+" y="+y +" type="+myType+"]");
}
}
ArrayList<MyObject> myArrayList = new ArrayList<MyObject>();
void setup()
{
size(100, 60);
myArrayList.add(new MyObject(3.5, 4.5, 6, "a"));
myArrayList.add(new MyObject(5.4, 2.6, 4, "b"));
}
For example, if I want to verify if there is an object which has the coordinates (3.5, 4.5), how should I proceed ?
Is there an easy way to do this ?
thanks for your help
The javadoc of List#contains(Object) states
Returns true if this list contains the specified element.
That's not what you're trying to do here. You're not specifying an element, you want to specify properties of an element. Don't use this method.
The long form solution is to iterate over the elements in the List and check them individually, returning true as soon as you find one, or false when you run out of elements.
public boolean findAny(ArrayList<MyObject> myArrayList, float targetX) {
for (MyObject element : myArrayList) {
if (element.x == targetX) { // whatever condition(s) you want to check
return true;
}
}
return false;
}
Since Java 8, there's a better way to do this using Stream#anyMatch(Predicate) which
Returns whether any elements of this stream match the provided predicate.
Where the given Predicate is simply a test for the properties you're looking for
return myArrayList.stream().anyMatch((e) -> e.x == targetX);
Regarding equality checks for floating point values, see the following:
What's wrong with using == to compare floats in Java?
Test for floating point equality. (FE_FLOATING_POINT_EQUALITY)
You can override equals function to define equal:
public class MyObject {
public float x;
public float y;
public int iD;
public String myType;
public MyObject (float x, float y, int iD, String myType)
{
this.myType = myType;
this.iD = iD;
this.x = x;
this.y = y;
}
#Override
public String toString() {
return ("[iD="+iD+" x="+x+" y="+y +" type="+myType+"]");
}
#Override
public boolean equals(Object o) {
if (o instanceof MyObject) {
MyObject myObject = (MyObject) o;
return myObject.iD == this.iD && myObject.myType.equals(this.myType);
}
return false;
}
}
Attention:
I must admit it's dangerous way to do this. override equals maybe will cause strange issues in program if you have used equals do some other compares. but in special case, maybe you can do that.
I have an ArrayList in Java :
{"PatMic", "PatientDoc", "Phram", "Patnet", "PatientA"}
All the elements have a number assigned : PatMic = 20, PatientDoc = 30, Phram = 40, Patnet = 50, PatientA = 60.
And my current Comparator :
Comparator<String> comparator = new Comparator<String>() {
#Override
public int compare(final String o1, final String o2) {
final int numbr1 = getElementNumber(); //Returns element's number in a list
final int numbr2 = getElementNumber();
if (numbr1 > numbr2 ) {
return 1;
} else if (numbr1 < numbr2 ) {
return -1;
}
return 0;
}
};
Collections.sort(strings, comparator);
I do not want to change the assigned numbers to each element but would want to move the element PatientA in between PatMic and PatientDoc so the modified list should look like :
{"PatMic", "PatientA" "PatientDoc", "Phram", "Patnet"}
Could someone please suggest how to achieve this? I tried many ways to modify the existing Comparator logic but in vain. Thank you.
You are trying to sort based on some inherent value associated with a String. Therefore, sorting on a String itself is probably not correct. What you probably want to use is either a custom object (implement equals, hashCode and the interface Comparable), or an enum type. This will allow you to change the internal state of these objects explicitly, which will manifest itself naturally when using a Comparator. For example, using a class:
class MyClass implements Comparable
{
private String name;
private int value;
//Constructor
public MyClass(String s, int v)
{
name = s;
value = v;
}
//Getters and setters
//Implement comparing method
}
Then you can use these objects in place of your Strings:
//...
MyClass patMic = new MyClass("PatMic", 20);
// So on..
First, you should give you comparator sufficient knowledge about what it should do. I mean you should have some data available to comparator that says something like "okay, sort them all by associated number except this one - place it right here". "Right here" could be anything that points exact position, I gonna choose "before that element".
So here we go
public void sortWithException(List<String> data, final Map<String, Integer> numbers, final String element, final String next) {
Collections.sort(data, new Comparator<String>() {
#Override
public int compare(String first, String second) {
if (first.equals(element) || second.equals(element)) { //the exception
Integer nextNumber = numbers.get(next);
Integer firstNumber = numbers.get(first);
Integer secondNumber = numbers.get(second);
if (first.equals(element)) {
if (next == null) // placing the exception after ANY element
return 1;
return secondNumber >= nextNumber ? -1 : 1; //placing the element before next and after all next's predecessors
} else { // second.equals(element)
if (next == null)
return -1;
return firstNumber >= nextNumber ? 1 : -1;
}
} else { //normal sort
return numbers.get(first) - numbers.get(second);
}
}
});
}
and call it like sortWithException(data, numbers, "PatientA", "PatientDoc")
Note that i used Map for associated numbers, you should probably use your own method to get those numbers.
I have been trying without any luck to make a list of all points in a model. When i execute this
HashList<Point> points=new HashList<Point>(16);
//add +y side
points.add(new Point(-5.0,5.0,-5.0));
points.add(new Point(-5.0,5.0,5.0));
points.add(new Point(5.0,5.0,5.0));
points.add(new Point(-5.0,5.0,-5.0));
points.add(new Point(5.0,5.0,5.0));
points.add(new Point(5.0,5.0,-5.0));
//add -x side
points.add(new Point(-5.0,5.0,-5.0));
points.add(new Point(-5.0,-5.0,-5.0));
points.add(new Point(-5.0,-5.0,5.0));
points.add(new Point(-5.0,5.0,-5.0));
points.add(new Point(-5.0,-5.0,5.0));
points.add(new Point(-5.0,5.0,5.0));
int length=points.length(); //equals 12, 6 expected
Point a=new Point(-5.0,5.0,-5.0);
Point b=new Point(-5.0,5.0,-5.0);
int aHashCode=a.hashCode(); //-737148544
int bHashCode=b.hashCode(); //-737148544
boolean equals=a.equals(b); //true
points containts 12 points which is the number I started with. I want all duplicates found which should result in only 6 points in table.
if (map.containsKey(e)) {
in HashList for some reason never gets executed. Any ideas?
HashList:
package dataTypes;
import java.util.ArrayList;
import java.util.HashMap;
public class HashList<E> {
private HashMap<E,Integer> map;
private ArrayList<E> data;
private int count=0;
public HashList() {
map=new HashMap<E,Integer>();
data=new ArrayList<E>();
}
public HashList(int size) {
map=new HashMap<E,Integer>(size);
data=new ArrayList<E>(size);
}
public int add(E e) { //returns key
if (map.containsKey(e)) {
return map.get(e);
} else {
map.put(e, count);
data.add(count,e);
return count++;
}
}
public int getKey(E e) {
return map.get(e);
}
public E get(int key) {
return data.get(key);
}
public int length() {
return count;
}
}
Point:
package geometry3D;
/**
* 3D point location or vector
*
* #author Matthew Cornelisse
* #version 2014-09-02-004500
*/
public class Point
{
// instance variables - replace the example below with your own
public double x;
public double y;
public double z;
/**
* Constructor for objects of class Point
*/
public Point()
{
// initialise instance variables
x = 0;
y = 0;
z = 0;
}
public Point(double x, double y, double z)
{
this.x=x;
this.y=y;
this.z=z;
}
public Point(Point a) {
x=a.x;
y=a.y;
z=a.z;
}
/**
* Normailizes the point to have distance from center of 1
*
*/
public void normalize()
{
// put your code here
double length=Math.sqrt(x*x+y*y+z*z);
x/=length;
y/=length;
z/=length;
}
//implements Shape
public void rotateX(double angle){
double newY=Math.cos(angle)*y-Math.sin(angle)*z;
double newZ=Math.sin(angle)*y+Math.cos(angle)*z;
y=newY;
z=newZ;
}
public void rotateY(double angle){
double newX=Math.cos(angle)*x-Math.sin(angle)*z;
double newZ=Math.sin(angle)*x+Math.cos(angle)*z;
x=newX;
z=newZ;
}
public void rotateZ(double angle){
double newX=Math.cos(angle)*x-Math.sin(angle)*y;
double newY=Math.sin(angle)*x+Math.cos(angle)*y;
x=newX;
y=newY;
}
public void rotate(Vector axis, double angle){
//source: http://inside.mines.edu/fs_home/gmurray/ArbitraryAxisRotation/
double oldX=x;
double oldY=y;
double oldZ=z;
double sinA=Math.sin(angle);
double cosA=Math.cos(angle);
Point offset=axis.offset();
Point vector=axis.vector();
double u=vector.x;
double v=vector.y;
double w=vector.z;
double a=offset.x;
double b=offset.y;
double c=offset.z;
x=(a*(v*v+w*w)-u*(b*v+c*w-u*oldX-v*oldY-w*oldZ))*(1-cosA)+oldX*cosA+(-c*v+b*w-w*oldY+v*oldZ)*sinA;
y=(b*(u*u+w*w)-v*(a*u+c*w-u*oldX-v*oldY-w*oldZ))*(1-cosA)+oldY*cosA+(c*u-a*w+w*oldX-u*oldZ)*sinA;
z=(c*(u*u+v*v)-w*(a*u+b*v-u*oldX-v*oldY-w*oldZ))*(1-cosA)+oldZ*cosA+(-b*u+a*v-v*oldX+u*oldY)*sinA;
}
public void move(double x, double y, double z){
this.x+=x;
this.y+=y;
this.z+=z;
}
public void move(Vector direction,double magnitude){
this.x+=(direction.vector().x*magnitude);
this.y+=(direction.vector().y*magnitude);
this.z+=(direction.vector().z*magnitude);
}
public boolean equals(Point compare) {
if (Math.abs(compare.x-x)>5*Math.ulp(compare.x)) return false;
if (Math.abs(compare.y-y)>5*Math.ulp(compare.y)) return false;
if (Math.abs(compare.z-z)>5*Math.ulp(compare.z)) return false;
return true;
}
public boolean equals(Point compare, double error) {
if (Math.abs(compare.x-x)>error) return false;
if (Math.abs(compare.y-y)>error) return false;
if (Math.abs(compare.z-z)>error) return false;
return true;
}
public int hashCode(){
Double a=(Double)x;
Double b=(Double)y;
Double c=(Double)z;
return a.hashCode()^Integer.rotateRight(b.hashCode(),12)^Integer.rotateRight(c.hashCode(),24);
}
public boolean equals(Object compare) {
try {
Point temp=(Point)compare;
if (temp.x!=x) return false;
if (temp.y!=y) return false;
if (temp.z!=z) return false;
return true;
} finally {
return false;
}
}
}
As noticed by rolfl, it is no use to try to use java equals method to compare Points with an acceptable error (see TL/DR section below if you are in doubt).
So, you have to do it the hard way. Do not even imagine to use map.containsKey(e), but create an explicit method in HashList. I would begin by that interface :
public interface DistEquality<E> {
boolean distEquals(E compare);
}
Then declare Point to implement it :
public class Point implements DistEquality<Point> {
...
public static double defaultError = 10E-3;
#Override
public boolean distEquals(Point compare) {
return equals(compare, defaultError);
}
}
And modify HashList that way
public class HashList<E extends DistEquality<E>> {
...
public int distValue(E e) {
for (Entry<E, Integer> entry: map.entrySet()) {
if (e.distEquals(entry.getKey())) {
return entry.getValue();
}
}
return -1;
}
public int add(E e) { //returns key
int pos = distValue(e);
if (pos != -1) {
return pos;
} else {
map.put(e, count);
data.add(count,e);
return count++;
}
}
...
}
I have not tested anything, but I think the general idea should be Ok.
TL/DR
Below solution is plain wrong - (thanks to rolfl to noticing)
The equals method in class Point requires exact equality of doubles. You should instead have a static double defaultError in Point class, initialized at an appropriate value and then do :
public boolean equals(Object compare) {
return ((compare instanceof Point) ? equals(compare, defaultError) : false);
}
But as noticed by rolfl, this is not enough, because javadoc for Object.hashCode() states If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
It would be very hard to imagine an intelligent hash compatible with the above equality method. Worse, once one point gets a hashCode value, any other point is at a finite number of defaultError and you can imagine a finished suite of Points that are all 2 per 2 equals and so hashCode should be a constant.
Worse, as equals is required to be reflexive, symetric and transitive, all Points should be equal.
It looks that the idea of using equal that way is really a bad idea :-(
The problem is your finally block in equals(Object). It's always returning false, even if you're returning true from the try block.
You're getting confused because of this:
boolean equals=a.equals(b); //true
... but that's not calling the same equals method - it's calling equals(Point).
Your equals(Object) method should be written as:
#Override public boolean equals(Object compare) {
if (compare == this) {
return true;
}
if (compare == null || compare.getClass() != getClass()) {
return false;
}
Point temp = (Point) compare; // Guaranteed to work now
return temp.x == x && temp.y == y && temp.z == z;
}
Note that:
As noted elsewhere, you can't come up with an equality/hash which handles tolerance... if you do any significant arithmetic with these points, you're unlikely to have exact equality any more
Your class has public fields, which is pretty much always a bad idea
Your class is mutable, which is a bad idea from the point of view of code using the hash code - collections are much easier to use correctly when the element type is immutable
If you make your class final, you can use instanceof instead of the getClass() check - and again, that would help prevent someone introducing a mutable subclass. (Equality relationships across an inheritance hierarchy are generally painful anyway.)
The HashList implementation is incorrect if you want it to be able to contain duplicates. On your add, if the map already has the key, you just return the value..., so your duplicates will never get inserted.
If you are getting duplicates, that means that your Equals/HashCode for Point is likely screwed up.
Right now, your HashList doesnt actually allow duplicates, so you might as well just get rid of it and use a HashSet from java collections. Also, get a decent java IDE like netbeans, eclipse, IntellIJ and have it generate the equals/hashcode for you on your point class.
Coordinate[] coords = {
new Coordinate(3093, 3630), new Coordinate(3095, 3632), new Coordinate(3098, 3633),
new Coordinate(3101, 3633), new Coordinate(3104, 3631), new Coordinate(3106, 3629),
new Coordinate(3107, 3627), new Coordinate(3108, 3624), new Coordinate(3109, 3620),
new Coordinate(3108, 3617), new Coordinate(3106, 3614), new Coordinate(3102, 3613),
new Coordinate(3099, 3613), new Coordinate(3097, 3613), new Coordinate(3093, 3614),
new Coordinate(3090, 3617), new Coordinate(3087, 3619)
};
int random = Misc.random(coords.length - 1);
Coordinate coord = coords[random];
boolean found = false;
if (insidePlayers.size() < coords.length) {
if (spawnPoints.contains(coord)) {
found = false;
}
while (!found) {
random = Misc.random(coords.length - 1);
coord = coords[random];
if (!spawnPoints.contains(coord)) {
player.spawnPointX = coords[random].getX();
player.spawnPointY = coords[random].getY();
spawnPoints.add(coord);
found = true;
break;
}
}
}
else {
player.spawnPointX = coords[random].getX();
player.spawnPointX = coords[random].getY();
}
Basically what I am trying to do here is, if there are more clients than available coordinates (spots), then give each player his own coordinate (So other clients can't have the same coords).
But unfortunately it doesn't work, sometimes clients are getting the same coordinates.
Why is it happening? what did I do wrong?
Coordinate class:
public class Coordinate {
private int x = 0;
private int y = 0;
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
ArrayList:
public static ArrayList<Coordinate> spawnPoints = new ArrayList<Coordinate>();
So what's wrong there?
You need to override equals() in your Coordinate class.
#override
public void equals(Object o){
if(o==null)
return false;
if(!(o instanceof Coordinate)){
return false;
Coordinate newO = (Coordinate) o;
if(this.x == newO.x && this.y == newO.y)
return true
return false;
}
This is because ArrayList#contains uses ArrayList#indexOf() which is defined as:
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i])) /// <--- Uses .equals()
return i;
}
return -1;
}
Note: While you are at it also override hashCode() as well. You don't really need it in your case but its a good practice and will help you if you use hash based data structures. This will help.
So essentially you have a set of spawn points, and you want to spawn players on these points but no two players can have the same spawnpoint. A simpler way of doing this would be to remove a coordinate from the list once it has been given to a player. Not sure how you feed in the clients (players), please elaborate on that if this solution doesn't help.
Coordinate[] coords = {
new Coordinate(3093, 3630), new Coordinate(3095, 3632), new Coordinate(3098, 3633),
new Coordinate(3101, 3633), new Coordinate(3104, 3631), new Coordinate(3106, 3629),
new Coordinate(3107, 3627), new Coordinate(3108, 3624), new Coordinate(3109, 3620),
new Coordinate(3108, 3617), new Coordinate(3106, 3614), new Coordinate(3102, 3613),
new Coordinate(3099, 3613), new Coordinate(3097, 3613), new Coordinate(3093, 3614),
new Coordinate(3090, 3617), new Coordinate(3087, 3619)
};
public static List<Coordinate> coordinates = new ArrayList<>(Arrays.asList(coords));
public static final Random rnd = new java.util.Random();
if(!coordinates.isEmpty())
int randomIndex = rnd.nextInt(coordinates.size());
Coord randomCoord = coordinates.get(randomIndex);
player.spawnPointX = randomCoord.getX();
player.spawnPointY = randomCoord.getY();
coordinates.remove(randomIndex);
else
System.out.println("No more coordinates left to assign to player");
I think the problem is a combination of two factors: 1) you are allocating a new coords array each time into the method; and 2) your Coordinate class does not implement equals() and hashCode(). The reason these cause a problem is that the default implementation of equals() and hashCode() inherited from Object are based on object identity, not equality of values. The result is that if, in one call, the Coordinate with values of (say) x=3093 and y=3630 is added to spawnPoints, on the next call, a new Coordinate object with the same x and y values will test as not contained in spawnPoints.
I think this can be fixed in one of two ways:
Declare the coords array to be a static final field instead of allocating a new array each time into the method. Then object identity will suffice for your logic.
Implement Coordinate.equals(Object) and Coordinate.hashCode() so that Coordinate objects with the same x and y field values test as equal and have the same hash code.
Even if the first approach fixes the problem, you should strongly consider implementing equals and hashCode, particularly if you are adding Coordinate objects to other collections.
I want to declare a List<int[]> or Map<int[],Boolean> but it's very difficult because arrays in Java doesn't implement the equals() method. If two arrays a and b are equal, a.equals(b) returns false.
Although java.util.Arrays.equals() compares arrays for equality, how do I get a List to use that method for comparison instead of the screwed-up equals()?
By the way, the int[] is a array [x,y,z] describing a coordinate. I want to put a bunch of these coordinates into a List or Map.
Why not declare your own class for a point? e.g.
class Point3D {
int x, y, z;
public boolean equals() {
// logic
}
}
and then declare List<Point3D>.
A general solution is to wrap the array in a method that does implement equals (and hashCode and perhaps compare, possibly toString and other methods that might make sense) as you wish:
public final class IntArrayWrapper {
private final IntArrayWrapper[] values;
public IntArrayWrapper(int... values) {
if (values == null) {
throw new NullPointerException();
}
this.values = values;
}
#Override public boolean equals(Object obj) {
if (!(obj instanceof IntArrayWrapper)) {
return false;
}
IntArrayWrapper other = (IntArrayWrapper)obj;
return java.util.Arrays.equals(this.values, other.values);
}
#Override public int hashCode() {
return java.util.Arrays.hashCode(values);
}
public int[] getValues() {
return values;
}
[...]
}
In this specific case, using arrays to contain certain fixed data values is poor design. Do it properly:
public final class Point {
private final int x;
private final int y;
private final int z;
public static Point of(int x, int y, int z) {
return new Point(x, y, z);
}
private Point(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
#Override public boolean equals(Object obj) {
if (!(obj instanceof Point)) {
return false;
}
Point other = (Point)obj;
return
this.x == other.x &&
this.y == other.y &&
this.z == other.z;
}
#Override public int hashCode() {
int hash;
hash = x;
hash = hash*41+y;
hash = hash*41+z;
return hash;
}
[...]
}
First of all, this isn't legal syntax. List can only take a single generic type.
Second, I would say that if you're worried about doing things at this low a level you aren't thinking abstractly enough. An array of arrays or a List of Lists is common enough. People who create matrix classes for linear algebra use them all the time. But you leverage objects and Java best when you hide those implementation details from clients of your class. Try it for your case and see if it helps.
You could always use the existing Point3d class instead of an array.
edit: The Apache Commons Math library also has some good options.