Currently I have a method overloading the following method:
public boolean isHorizontalOrVertical(Point firstPoint, Point secondPoint) {
return firstPoint.getFirstComponent() == secondPoint.getFirstComponent()
|| firstPoint.getSecondComponent() == secondPoint.getSecondComponent();
}
public boolean isHorizontalOrVertical(List<Point> points) {
if (points == null || points.size() < 2) {
throw new IllegalArgumentException("invalid number of points");
}
Point start = points.get(0);
return points.stream()
.allMatch(p -> isHorizontalOrVertical(start, p));
}
The method is needed to check if two or three points are vertical/horizontal to each other. In the case of three points, it just has to check if the two last points are horizontal/vertical to the start point.
Does anyone have any idea how I can get it all into just one method?
First and foremost I have to note the fact that it doesn't make sense, to me at least, a method which calculates if two entities are horizontal or vertical and those entities are Points. How can two points be horizontal or vertical?
isHorizontalOrVertical is a bad name
Overcoming the above, you could create a single method which calculates if two points are horizontal or vertical.
Change the name isHorizontalOrVertical because it's redundant. A better name is isHorizontal or isVertical. The method will return a boolean so if isHorizontal returns false, then it's vertical and vice versa. Probably a better name could be areTwoPointsHorizontal but I am having trouble even writing that because it transmits the wrong message, but feel free to choose your own.
So the method,
public boolean isHorizontal(Point first, Point second){
boolean sameFirstComponents = firstPoint.getFirstComponent() ==
secondPoint.getFirstComponent();
boolean sameSecondComponents = firstPoint.getSecondComponent() ==
secondPoint.getSecondComponent();
return sameFirstComponents || sameSecondComponents;
}
Finally, create a method which calculates if an arbitary number of points in a list are all between them horizontal or vertical (assuming if point A is horizontal with point B, then if point C is horizontal with B, so is with A).
Oveload that method since it does the same thing and the only thing changing are the parameters. (Note the use of the simple isHorizontal method from above)
public boolean isHorizontal(List<Point> points){
boolean allPointsHorizontal = true;
for (int i=0; i<points.size(); i++) {
boolean nextPointExists = i<points.size() - 1;
if (nextPointExists) {
Point current = points.get(i);
Point next = points.get(i+1);
allPointsHorizontal = allPointsHorizontal && isHorizontal(current,next);
if (!allPointsHorizontal)
return false;
}
}
return allPointsHorizontal;
}
You can have just one method as follows:
public boolean isHorizontalOrVertical(List<Point> points) {
if (points == null || points.size() < 2) {
throw new IllegalArgumentException("invalid number of points");
}
if (points.size() == 2) {
return points.get(0).getFirstComponent() == points.get(1).getFirstComponent()
|| points.get(0).getSecondComponent() == points.get(1).getSecondComponent();
}
Point start = points.get(0);
return points.stream()
.allMatch(p -> isHorizontalOrVertical(List.of(start, p)));
}
Note: If you are not using Java version >= 9, please use Arrays.asList instead of List.of.
The method could be implemented like this:
public boolean isHorizontalOrVertical(Point firstPoint, Point secondPoint, Point thirdPoint) {
return isHorizontalOrVertical(Arrays.asList(firstPoint, secondPoint, thirdPoint));
}
Your isHorizontalOrVertical(List<Point>) method will fail when the list is empty, and the call doesn't make much sense when the list has only one element.
I think a better way to do this is with two required parameters, plus a variadic parameter, so that callers must at least give 2 points.
private boolean are2PointsHorizontalOrVertical(Point firstPoint, Point secondPoint) {
return firstPoint.getFirstComponent() == secondPoint.getFirstComponent()
|| firstPoint.getSecondComponent() == secondPoint.getSecondComponent();
}
public boolean arePointsHorizontalOrVertical(Point point1, Point point2, Point... rest) {
return are2PointsHorizontalOrVertical(point1, point2) &&
Arrays.stream(rest).allMatch(x -> are2PointsHorizontalOrVertical(point1, x));
}
This technically is still "one method" as far as the public interface is concerned. You can substitute the helper are2PointsHorizontalOrVertical back into the public method if you really want, but I don't see any benefit in doing that.
Actually, you can have only one method:
public boolean isHorizontalOrVertical(Point firstPoint, Point secondPoint, Point... others) {
// check firstPoint, secondPoint for null is ommited
if (others == null || others.length == 0) {
return firstPoint.getFirstComponent() == secondPoint.getFirstComponent()
|| firstPoint.getSecondComponent() == secondPoint.getSecondComponent();
} else {
// First, create a stream with second point + others elements
// then match against the first point
return Stream.of(new Point[]{secondPoint}, others).flatMap(e -> Stream.of(e))
.allMatch(p -> isHorizontalOrVertical(firstPoint, p));
}
}
Related
I have a list of objects, lets say Shapes.
I would like to process them using a stream and return another object - ShapeType - based on what is in the list.
Normally I will just return ShapeType.GENERIC, however if there is a Rectangle in there, I would like to return ShapeType.RECT. If there is a hexagon in a list I would like to return ShapeType.HEXA.
When both rectangle and square are present, I would like to return ShapeType.HEXA.
Now, when it comes to code I would like something like this:
public ShapeType resolveShapeType(final List<Shape> shapes) {
shapes.stream()
.filter(shape -> shape.getSideCount() == 6 || shape.getSideCount() == 4)
// I should have a stream with just rectangles and hexagons if present.
// what now?
}
You can use
public ShapeType resolveShapeType(final List<Shape> shapes) {
int sides = shapes.stream()
.mapToInt(Shape::getSideCount)
.filter(count -> count==4 || count==6)
.max().orElse(0);
return sides==6? ShapeType.HEXA: sides==4? ShapeType.RECT: ShapeType.GENERIC;
}
This maps each element to its side count and reduces them to the preferred type, which happens to be the maximum count here, so no custom reduction function is needed.
This isn’t short-circuiting, but for most use cases, it will be sufficient. If you want to reduce the number of operations to the necessary minimum, things will be more complicated.
public ShapeType resolveShapeType(final List<Shape> shapes) {
OptionalInt first = IntStream.range(0, shapes.size())
.filter(index -> {
int count = shapes.get(index).getSideCount();
return count == 6 || count == 4;
})
.findFirst();
if(!first.isPresent()) return ShapeType.GENERIC;
int ix = first.getAsInt(), count = shapes.get(ix).getSideCount();
return count==6? ShapeType.HEXA: shapes.subList(ix+1, shapes.size()).stream()
.anyMatch(shape -> shape.getSideCount()==6)? ShapeType.HEXA: ShapeType.RECT;
}
We know that we can stop at the first HEXA, but to avoid a second pass, it’s necessary to remember whether there was an occurence of RECT for the case there is no HEXA. So this searches for the first element that is either, a RECT or HEXA. If there is none, GENERIC is returned, otherwise, if the first was not a HEXA, the remaining elements are checked for an element of the HEXA kind. Note that for processing the remainder after the first RECT, no filter is needed as it is implied that shapes that are neither, RECT nor HEXA, can’t fulfill the condition.
But it should also be obvious that this code, trying to minimize the numbers of checks, is harder to read than an equivalent for loop.
Assuming that only the three types of shapes can be present in the list, an alternative would be:
Set<Integer> sides = shapes.stream()
.map(Shape::getSideCount)
.collect(toSet());
if (sides.contains(6)) return HEXA;
else if (sides.contains(4)) return RECTANGLE;
else return GENERIC;
But I think the most straightforward (and efficient) way would be a good old for loop:
ShapeType st = GENERIC;
for (Shape s : shapes) {
if (s.getSideCount() == 6) return HEXA;
if (s.getSideCount() == 4) st = RECTANGLE;
}
return st;
If I understand what you're trying to do, then you can use anyMatch. Like,
public ShapeType resolveShapeType(final List<Shape> shapes) {
if (shapes.stream().anyMatch(shape -> shape.getSideCount() == 6)) {
return ShapeType.HEXA;
} else if (shapes.stream().anyMatch(shape -> shape.getSideCount() == 4)) {
return ShapeType.RECT;
} else {
return ShapeType.GENERIC;
}
}
One way to do this (streaming shapes once) would be to preserve the shape presence with an array. Like,
public ShapeType resolveShapeType(final List<Shape> shapes) {
boolean[] bits = new boolean[2];
shapes.stream().forEach(shape -> {
int sides = shape.getSideCount();
if (sides == 4) {
bits[0] = true;
} else if (sides == 6) {
bits[1] = true;
}
});
if (bits[1]) {
return ShapeType.HEXA;
} else if (bits[0]) {
return ShapeType.RECT;
} else {
return ShapeType.GENERIC;
}
}
Can also do something like this:
ShapeType r = shapes.stream()
.map(s -> ShapeType.parse(s.getSides()))
.filter(c -> c == ShapeType.Hexagon || c==ShapeType.Square)
.max(ShapeType::compareTo)
.orElse(ShapeType.Generic);
Here, I've taken a little liberty with your ShapeType:
enum ShapeType {
Square(4), Hexagon(6), Generic(Integer.MAX_VALUE);
int v;
ShapeType(int v) {
this.v = v;
}
static ShapeType parse(int v) {
switch (v) {
case 4: return Square;
case 6: return Hexagon;
default:
break;
}
return Generic;
}
public String toString(){
return Integer.toString(v);
}
}
TBH you can avoid the parse operation if you add a getShapeType() method which returned the correct type per Derived type. Then the map() operation will only extract the type, for example .map(Shape::getShapeType).
The .filter() will find the group you are interested in, the largest shape is deemed the label of the collection...
Sounds like a case for 'reduce' or 'collect/max'.
Assume you have a method that selects the 'dominant' type (you can put it in a lambda, but IMHO it's more readable as a method):
public class Util{
public static ShapeType dominantType(ShapeType t1, ShapeType t2){
if(t1==HEXA || t2==HEXA) return HEXA;
else if (t1==RECTANGLE || t2==RECTANGLE) return RECTANGLE;
else return GENERIC;
}
}
There are several ways to use it, one reduce example would be:
shapes.stream()
.filter(shape -> shape.getSideCount() == 6 || shape.getSideCount() == 4)
.map(shape -> shape.getSideCount()==6? HEXA:RECTANGLE)
.reduce( GENERIC, Util::dominantType);
// using GENERIC in case of empty list
You may also want to look into collectors.maxBy.
BTW whatever approach you take, please give some thought to the behavior in case of an empty list...
I have an abstract class called DrawingObject that is extended by four subclasses: Point, Line, FreeFormLine, and Circle. DrawingObject implements Comparable and I have defined the compareTo method to look like this
public int compareTo(DrawingObject object)
{
if(object instanceof Point && this instanceof Point)
{
//determine which has a higher value
return 0;
}
else if(object instanceof Point && this instanceof Line)
return 1;
else if(object instanceof Point && this instanceof FreeFormLine)
return 1;
else if(object instanceof Point && this instanceof Circle)
return 1;
else if(object instanceof Line && this instanceof Point)
return -1;
else if(object instanceof Line && this instanceof Line)
{
//determine which has a higher value
return 0;
}
else if(object instanceof Line && this instanceof FreeFormLine)
return 1;
else if(object instanceof Line && this instanceof Circle)
return 1;
else if(object instanceof FreeFormLine && this instanceof Point)
return -1;
else if(object instanceof FreeFormLine && this instanceof Line)
return -1;
else if(object instanceof FreeFormLine && this instanceof FreeFormLine)
{
//determine which has a higher value
return 0;
}
else if(object instanceof FreeFormLine && this instanceof Circle)
return 1;
else if(object instanceof Circle && this instanceof Point)
return -1;
else if(object instanceof Circle && this instanceof Line)
return -1;
else if(object instanceof Circle && this instanceof FreeFormLine)
return -1;
else if(object instanceof Circle && this instanceof Circle)
{
//determine which has a higher value
return 0;
}
return 0;
}
Now that I have this I would like to extend the code the sorting by value. I am confused as how to go about doing this. For example Point has two instance fields, double x and double y. I am confused as to which to sort by. I am also in confusion over the other types of objects and how to sort them. Each class has an equals method that is declared abstract in DrawingObject but implemented in each subclass.
Here are description of the fields in each class:
Point has two double fields, x and y. These represent the coordinates on a Cartesian coordinate grid of the point.
Line has two Point fields, p1 and p2. These represent the start and end points of the line.
FreeFormLine has one ArrayList field, points. This represents all of the points along the line.
Circle has one Point field, center, and one double field, radius. These represent the center point of and radius of the circle.
In conclusion, how do I determine which object has more or less value when there is more than one field to evaluate?
Edit:
The purpose of doing this sorting is allow me to search efficiently through an array of DrawingObjects using binary seaching.
The problem that you are trying to solve is much harder than ut seems, because you are trying to build a double dispatch method, i.e. a method that is virtual with respect to two objects.
There are ways to do that, for example, using a visitor pattern, but if you would be fine with establishing a fixed order across different classes (say, a point is always less than a rectangle, a rectangle is less than a line, etc.) you could make your code very consistent by making a protected method in your base class that returns the "sort order" for the class:
protected abstract int sortOrder();
Point would return 0, rectangle would return 1, line would return 2, and so on. Now your comparison method could call sortOrder() on both sides, and decide which one is less than the other if sort ordrrs are not the same.
If sort orders are the same, then the classes are the same. Implement comparable in each class, and forward the call to class-specific comparison method when classes are the same.
As far as comparison of the same class goes, you can decide on an arbitrary ordering among the attributes - say, x before y, height before width, etc. As long as you are consistent about applying your rules, your ordering is going to be fine.
I am currently trying to implement a binary search tree shape comparison and am having trouble with one line of my code.
if(treeStructOne.getHeight() == 1 && treeStructTwo.getHeight() == 1) //Base Case, if both are empty then they must be equal!
{
return true;
}
if(treeStructOne.getHeight() != treeStructTwo.getHeight()) //First make sure height is the same, if not, must be unequal
{
return false;
}
if(treeStructOne.hasLeft() && treeStructTwo.hasLeft())
{
return similar(treeStructOne.getLeft(),treeStructTwo.getLeft());
}
if(treeStructOne.hasRight() && treeStructTwo.hasRight()) //PROBLEM IS HERE
{
return similar(treeStructOne.getRight(),treeStructTwo.getRight());
}
return false;
The problem occurs when a node on Tree 1 and 2 has a left child, but only tree 1 has a right and not Tree 2. After it checks that they both have left children, it does not run the the check on right children if left is true. Is this to do with the way recursion works in java?
if both trees hasLeft() returns true then your method will return in that if clause. My guess is you want to assign the result from the similar call in the last two if clauses and after the if clause do something like
return leftSimilar && rightSimilar;
The first two if's will work, but the last part should capture the conditions of p implies q which is ~p or q for both left and right. In other words, if treeStructOne has a left subtree and treeStructTwo has a left subtree, then check to see if they are similar (return similar...)
if(treeStructOne.getHeight() == 1 && treeStructTwo.getHeight() == 1) //Base Case, if both are empty then they must be equal!
{
return true;
}
if(treeStructOne.getHeight() != treeStructTwo.getHeight()) //First make sure height is the same, if not, must be unequal
{
return false;
}
return (treeStructOne.hasLeft() && treeStructTwo.hasLeft()
? similar(treeStructOne.getLeft(),treeStructTwo.getLeft())
: false)
&& (treeStructOne.hasRight() && treeStructTwo.hasRight()
? similar(treeStructOne.getRight(),treeStructTwo.getRight())
: false);
The return statement immediately returns from the current method, i.e. the remainder of the method will not be executed.
In your case, you want to make two recursive calls before returning from the method. You can do this with:
boolean similarLeft;
if(treeStructOne.hasLeft() && treeStructTwo.hasLeft()) {
similarLeft = similar(treeStructOne.getLeft(),treeStructTwo.getLeft());
} else {
similarLeft = ?; // TODO what is good condition here?
}
then do the same for the right side and conclude with
return similarLeft && similarRight;
However, for truly ideomatic java, I'd do the null checks after invoking the method rather then before it, thereby reducing code duplication:
boolean similar(TreeStruct x, TreeStruct y) {
if (x == null) {
return y == null;
} else {
return y != null && similar(x.left, y.left) && similar(x.right, y.right);
}
}
This equals() method will produce the same output if lines 3-5 are omitted (I numbered these lines). What is the point of these lines?
/** Return true if that Beetle has the same parts as this one. */
public boolean equals(Object that) {
3. if (this == that) {
4. return true;
5. }
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
Beetle thatBeetle = (Beetle) that;
return body == thatBeetle.body
&& eyes == thatBeetle.eyes
&& feelers == thatBeetle.feelers
&& head == thatBeetle.head
&& legs == thatBeetle.legs
&& tail == thatBeetle.tail;
}
Checking == reference equality is fast, and if true the object is being compared to itself -- so is by definition equal.
This is often used a first step when comparing objects, as it's much faster than comparing all the details. But it's more commonly used from the caller/client function, rather than inside the equals() implementation.
For example, in a linear search:
public int indexOfBeetle (Beetle beetle, List<Beetle> list) {
for (int i = 0; i < list.size(); i++) {
Beetle cand = list.get( i);
if (cand == beetle || cand.equals( beetle))
return i; // Found.
}
// Not Found.
return -1;
}
The operator == checks if the objects are the same instance in memory whereas when you override equals you usually want to perform a logical test.
Let's take an example:
public class Person {
private String name;
// Here there are constructor and getters and setters
}
Now let's run these lines:
Person a = new Person();
Person b = a;
Person c = new Person();
If you compare these instances with == this is what you'll get:
a == a ==> true
a == b ==> true
a == c ==> false
Now, let's set the name:
a.setName("Joe"); // This also sets b because they're the same object
c.setName("Joe");
If our equals looks like this:
public boolean equals (Object other) {
if(other == this) return true;
if(other instanceof Person == false) return false;
if(this.getName().equals(((Person) other).getName())) return true;
}
So we'll now get that a.equals(c) is true even though a==c is false.
So, why do we have the first line? - Some objects' equality is more expensive to compute and by checking this condition at the beginning you might spare some unnecessary computations
Doing that are you checking if the two objects are pointing to the same memory address. Using .equals without it you will achieve the same result because if they are pointing to the same memory address, it's obviously they are equals. But doing that it's much more faster, that's why most developers put this lines on .equals
I defined this simple method:
public static boolean isBorder(int x, int y) throws CollisionDetectionException {
try {
if ( (levelItems[x][y] instanceof StaticGameObject && levelItems[x][y].isVisible()) ||
(levelItems[x-1][y] instanceof StaticGameObject && levelItems[x-1][y].isVisible()) ||
(levelItems[x][y+1] instanceof StaticGameObject && levelItems[x][y+1].isVisible()) ||
(levelItems[x][y-1] instanceof StaticGameObject && levelItems[x][y-1].isVisible()) ||
(levelItems[x-1][y-1] instanceof StaticGameObject && levelItems[x-1][y-1].isVisible()) ||
(levelItems[x-1][y+1] instanceof StaticGameObject &&levelItems[x-1][y+1].isVisible()) ||
(levelItems[x+1][y] instanceof StaticGameObject && levelItems[x+1][y].isVisible()) ||
(levelItems[x+1][y+1] instanceof StaticGameObject && levelItems[x+1][y+1].isVisible()) ||
(levelItems[x+1][y-1] instanceof StaticGameObject && levelItems[x+1][y-1].isVisible()) ) {
return true;
} else {
return false;
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new CollisionDetectionException("Collision couldn't be checked because checking position " + x + "/" + y + " caluclated values below (0/0)");
}
}
As you can see i have a 2dimensional array. Now i want to check a specific position ((x/y) -> the method arguments), if there are any visible StaticGameObject in the neighbouring fields of the 2dimensional array.
The levelItems array consists of so called GameObject. StaticGameObject is a a direct subclass of GameObject.
Any hints how to improve this method?
Add a method to GameObject
bool isBorderObject() { return false; }
Then override in StaticGameObject
bool isBorderObject() { return true; }
change the test to
(levelItems[x][y].isBorderObject() && levelItems[x][y].isVisible())
Also, the if could be this nested for
for (int i = x-1; i <= x+1; ++i) {
for (int j = y-1; j <= y+1; ++j) {
GameObject item = levelItems[i][j];
if (item.isBorderObject() && item.isVisible())
return true;
}
}
return false;
(Disclaimer: I've worked on Java games running on a lot of mobile devices)
People already showed how to simplify all these if statements. I'd add that defining your own CollisionDetectionException is probably completely overkill.
For a start, such low-level Java details don't exist at the OO level: exceptions, especially checked exceptions, are a Java idiosynchrasies that is really not necessary. Some very good and very impressive Java frameworks do mostly away with them, like Spring. Then a lot of very impressive and powerful apps, made of millions and millions of lines of code, run perfectly fine without ever using the concept of checked exception because they're, well, written in language in that don't have this kind of concept (once again: a "checked exception" doesn't exist at the OO level, hence any OOA/OOD to OOP without ever needing to use checked exception).
In any case: there's really no reason to turn an ArrayIndexOutOfBoundException into your own specific checked exception. This means you plan to use exception for flow control, which is a HUGE no-no.
Then, regarding your "isBorder" test... You probably don't need that. You think you do, but you really don't. Why do you want to know if it's a "border"? To detect a collision I guess.
You're using a static method to detect if it's a border, and static is the anti-thesis of OO. What is important is the messages you are passing around between objects. You may have all your objects responding to some isCollidingWith(...) message: objects that are "border" knows they are border, they'll know how to deal with a isCollidingWith(...) message.
Then you can go even further: instead of doing a isCollidingWith(...) you could have some resolveCollisionWith(...) method.
public class Wall implements MySuperAbstraction {
int wallHitPoints = 42;
boolean isWallStillUp = true;
void resolveCollisionWith( SomeObject o ) {
if ( o.isStrongEnoughToHarmWall ) {
wallHitPoints--;
isWallStillUp = wallHitPoints > 0;
}
}
}
This is just a quick piece of code and it doesn't deal with the fact that SomeObject needs probably to bounce when he hits the wall, etc. but the point stands: in OO objects knows how to communicate between themselves. They know how to deal with the various messages passed around.
If you want to do OO, then all I can tell you is that Java's static, instanceof and checked exceptions are definitely not the way the go. Basically, they're the anti-thesis of OO :)
]
This should get rid of the extremely large if block you have:
for(int col = x-1; col <= x+1; col++)
{
for(int row = y-1; row <= y+1; row++)
{
if(levelItems[row][col] instanceof StaticGameObject && levelItems[row][col].isVisible())
return true;
}
}
(This solution merely reduces the crazy if, not get rid of the instanceof, as you can see)
Of course, in both my example and yours, you should make sure to check for array bounds problems.
A revision to #Lou's solution which is to have one method and one loop.
for (int i = 0; i < 9; i++)
if (levelItems[x-1 + i/3][y-1 + i%3].isBorderObjectVisible())
return true;
return false;
Think about "inversion of control".
As an example, how could introducing this method to the GameObject class:
public boolean
isBorder()
{
return false;
}
and this override in the StaticGameObject class:
public boolean
isBorder()
{
return self.isVisible();
}
simplify the code above?
Another nice technique is to have a function that returns the set of adjacent cells. In this way you avoid (or anyway move) the double-loop. It's better separation of concerns; when you discover that your algorithm was forgetting about out-of-bounds conditions, there's exactly one place to fix it.
public Set<GameObject> adjacentItems(int x, int y) {
Set<GameObject> set = new HashSet<GameObject>();
for (int i = -1; i < 2; ++i)
for (int j = -1; j < 2; ++j)
set.add(levelItems[x+i][y+j]);
return set;
}
public boolean isBorder(int x, int y) {
for (GameObject item : adjacentItems(x, y))
if (item.isBorder() && item.isVisible())
return true;
return false;
}
Don't make this any more complex. Simply be sure all subclasses of GameObject implement isVisible. That's all it takes.
If you need to also distinguish moveable vs. non-movable, then you need two methods:
isVisible -- visible or not
isMovable -- moveable or not
You never need instanceof.
[It's not clear from the question, but it appears that the class StaticGameObject actually means is a subclass of GameObject with isMovable() false.]