How to combine and return enum value - java

I have a coordinate system such as this:
public enum Direction {
N ( 0, 1),
NE ( 1, 1),
E ( 1, 0),
SE ( 1, -1),
S ( 0, -1),
SW (-1, -1),
W (-1, 0),
NW (-1, 1);
private int x = 0, y = 0;
private Direction(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Direction combine(Direction direction) {
//unsure
}
}
I'm trying to combine directions with a method within the enum, like:
Direction.N.combine(Direction.E) -> should become Direction.NE
Direction.N.combine(Direction.N) -> null or Direction.N again
My thoughts are to loop through all the values in the enum, and find one that matches its x and y combined:
public Direction combine(Direction direction) {
Direction[] directions = Direction.values();
for (int i = 0; i < directions.length; i++)
if (x + direction.x == directions[i].x && y + direction.y == directions[i].y)
return directions[i];
return this;
}
But I feel like that's an inefficient way to approach this. Is there another way to combine these directions that doesn't involve looping through all the enums?
I also want to create an uncombine function that will reverse the combine.
Direction.NE.uncombine() -> Direction[] {Direction.N, Direction.E}
I could also use the same looping technique, like:
public Direction[] uncombine() {
Direction[] directions = Direction.values(),
rtn = new Direction[2];
for (int i = 0; i < directions.length; i++)
if (x == directions[i].x && directions[i].y == 0)
rtn[0] = directions[i];
for (int i = 0; i < directions.length; i++)
if (y == directions[i].y && directions[i].x == 0)
rtn[1] = directions[i];
return rtn;
}
So is there a more efficient way that I could try out?

I think that creating a Map<Direction, Direction> for each enum value is going to give you a good balance between performance and code neatness.
The combine method becomes:
public Direction combine(Direction other) {
return this.combinerMap.get(other);
}
Of course, you need to build the maps during initialization of the enum class.
Returning null from this method is a bad idea because it pushes the responsibility for sanity checking back onto the caller. So I'd write it like this:
public Direction combine(Direction other)
throws InsaneDirectionsException{
Direction res = this.combineMap.get(other);
if (res == null) {
throw new InsaneDirectionsException(
"Can't combine directions " + this +
" and " + other);
}
return res;
}

If your real class is as simply as the one from the question I think that the most efficient way would be to either manually "hardcode" or pre-calculate (e.g. in static init block) the relationship between argument and result and keep it in map and then only refer to already existing results.

You can keep Map<Byte, Map<Byte, Direction>> where x and y will be indexes. Once you compute new x and y, obtaining Direction will be as simple as matrix.get(x).get(y).

Related

A* (A Star) - Algorithm | How to go back when there are walls?

I'm currently trying to implement the A* (A star) - Algorithm. I already got it working when I don't have walls and when it is only needed to go besides the wall. My problem now is that when I'm placing the start inside of my walls, my algorithm is calculating infinitely as I think it doesn't go backward. Can you guys please help me?
Here is the code from the node class:
int[][] mMap; // if there is a wall => 1
int[][] mAStarField;
ArrayList<AStarNode> mAStarPath;
public class AStarNode implements Comparable<AStarNode>{
public int x;
public int y;
public float c;
public AStarNode p;
public AStarNode(int x, int y, float c, AStarNode p) {
this.x = x; //X pos
this.y = y; //Y pos
this.c = c; //Cost to get to the node
this.p = p; //Parent of the node
}
//override the compareTo method
public int compareTo(AStarNode node)
{
if (c == node.c)
return 0;
else if (c > node.c)
return 1;
else
return -1;
}
}
public class Node {
public int x;
public int y;
public int z;
public int w;
public Node(int x, int y, int z, int w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
}
This is the code from my A* algorithm:
//Pathfinding with A*
//return path length
int updateAStar() {
//Needed for drawing:
//Array containing the distance to the start node (filled with max int at start)
mAStarField = new int[mMap.length][mMap[0].length];
//List containing the found path
mAStarPath = new ArrayList<AStarNode>();
for (int j = 0; j < mMap.length; j++) {
for (int k = 0; k < mMap[0].length; k++) {
mAStarField[j][k]=Integer.MAX_VALUE;
}
}
//AStarNode(x,y,c,w)
//x X pos
//y Y pos
//c Cost to get to the node
//p Parent of the node
//List can be sorted expensive but simple by c value with
//Collections.sort(openList);
ArrayList<AStarNode> openList = new ArrayList<AStarNode>();
ArrayList<AStarNode> closedList = new ArrayList<AStarNode>();
int dist = abs(mStartNode[0]-mEndNode[0])+abs(mStartNode[1]-mEndNode[1]);
//If there is any target, that isn't on my field, add start node to list
if (dist>0 && mMap[mStartNode[0]][mStartNode[1]] != 1 && mMap[mEndNode[0]][mEndNode[1]]!=1) {
openList.add(new AStarNode(mStartNode[0], mStartNode[1], 0, null));
mAStarField[mStartNode[0]][mStartNode[1]] = 0;
}
// my code begins here (only everything from here on can be edited!)
while(!openList.isEmpty())
{
Collections.sort(openList);
AStarNode current = openList.get(0);
if(current.x == mEndNode[0] && current.y == mEndNode[1])
{
return 1;
}
openList.remove(0);
closedList.add(current);
ArrayList<AStarNode> neighbors = new ArrayList<AStarNode>();
neighbors.add(new AStarNode(current.x - 1, current.y, current.c + 1, current));
neighbors.add(new AStarNode(current.x + 1, current.y, current.c + 1, current));
neighbors.add(new AStarNode(current.x, current.y - 1, current.c + 1, current));
neighbors.add(new AStarNode(current.x, current.y + 1, current.c + 1, current));
for(AStarNode n : neighbors)
{
if(n.x >= 0 && n.y >= 0 && n.x < mMap.length && n.y < mMap.length && mMap[n.x][n.y] != 1){
float cost = estimateDistanceEnd(n.x, n.y);
n.c = cost;
if(closedList.contains(n) && cost >= n.c) continue;
if(!openList.contains(n) || cost < n.c)
{
n.p = current;
if(!openList.contains(n)){
mAStarField[n.x][n.y] = (int) n.c;
openList.add(n);
Collections.sort(openList);
}
}
}
}
}
return -1;
}
int estimateDistanceEnd(int x, int y){
return abs(x-mEndNode[0])+abs(y-mEndNode[1]);
}
int estimateDistanceStart(AStarNode a){
return abs(a.x-mStartNode[0])+abs(a.y-mStartNode[1]);
}
int estimateDistance(AStarNode a, AStarNode b){
return abs(a.x-b.x)+abs(a.y-b.y);
}
A picture of my current path solving result
Important: I'm only allowed to change code inside of the area I marked.
Thank you!
I can't run the code, but I have a little idea about the the source of the issue. See, the A* algorithm doesn't "walk back" while looking for the best path. It solves the pathfinding issue by calculating the less costly way to get to the end for literally every node that it evaluates. It starts by calculating the itineraries which are the most straightforward, then if it didn't work it'll enlarge it's options until it either, uh, finds a way - or run out of options.
The principle of the closed list to avoid evaluating a node twice. As you guessed, the problem here is that you are creating new nodes for neighbors at every iteration of the pathfinding algorithm, thus making it harder for the closed list to be used correctly.
A complex object like a custom class can be compared through 3 means: it either is the same object (it refer to the same pointer (it's the same instance, it's at the same place in the computer's memory)), or the values are all the same whatever it's pointer is pointing, or you can define a rule to compare them. These methods would be: comparing by reference, comparing by value and operator overloading - although that last one isn't possible in java, but you can write a method to do the same.
When doing closedList.contains(n), you are comparing by reference (which is the default for this kind of operation). Since all the nodes have been created on the fly, even if their coordinates are the same they all have different address in memory, and this is why this condition will never be met.
Assuming that you cannot mess with your tutor's code, you can still fix this. You almost got it right the first time! There are many ways to to fix this, in fact, and as I miss some of the context I'll be rather plain in my suggested approach: you'll write a method to fetch a specific node from a list (like the operator overloading I spoke about, but with the bare minimum effort) and we'll work by reference from this point onward.
First, create a master list of all your AStarNode (if you don't already have one, if you do then use that one instead):
// my code begins here (only everything from here on can be edited!)
ArrayList<AStarNode> nodesList = new ArrayList<AStarNode>();
for (int j = 0; j < mapWidth; j++) {
for (int k = 0; k < mapHeight; k++) {
nodesList.add(new AStarNode(j, k)); // I gimmicked the constructor for my own confort, you'll have to tweak this line so it fits in your code
}
}
Then, write yourself a method which will return a node from an array based on given xy coordinates:
AStarNode GetAStarNodeByPosition(int x, int y, ArrayList<AStarNode> list) {
for (AStarNode m : list) {
if (m.x == x && m.y == y) {
return m;
}
}
return null;
}
Now, you can use these to compare all your nodes by reference. So, now instead of instantiating new nodes all the time, you'll always fetch them from the master list, by reference:
ArrayList<AStarNode> neighbors = new ArrayList<AStarNode>();
neighbors.add(GetAStarNodeByPosition(current.x - 1, current.y, nodesList));
neighbors.add(GetAStarNodeByPosition(current.x + 1, current.y, nodesList));
neighbors.add(GetAStarNodeByPosition(current.x, current.y - 1, nodesList));
neighbors.add(GetAStarNodeByPosition(current.x, current.y + 1, nodesList));
Also, don't forget to fix this line:
//openList.add(new AStarNode(mStartNode[0], mStartNode[1], 0, null));
openList.add(GetAStarNodeByPosition(mStartNode[0], mStartNode[1], nodesList));
Lastly, always remember to test for null if you know that you may have some in your arrays. In this case, the GetAStarNodeByPosition method can return a null if you're too close to the boundaries of the maze. You can either modify the way you add to the neighbors list so there will be no null in there, or you can check for null on this line:
if(n != null && n.x >= 0 && n.y >= 0 && n.x < mMap.length && n.y < mMap.length && mMap[n.x][n.y] != 1){
Honestly I would prevent the inclusion of null in the array at all, that's much safer if you modify the code further later.
Now all your nodes will relate and you'll be able to overcome obstacles which needs your algorithm to search in a more clever way than a straight line.
Have fun!

Using BFS to find number of possible paths for an object on a grid

I have a matrix that represents a grid and would like to find out all possible places an object can move to.
An object can only move horizontally or vertically.
Let's assume that the example below is the grid I'm looking at, which is represented as a 2d matrix. The object is the *, the 0s are empty spaces that an object can move to, and the 1s are walls which the object cannot jump over or go on to.
What is the best way to find all possible movements of this object provided that it can only move horizontally or vertically?
I'd like to print a message saying: "There are 9 places the object can go to." The 9 is for the example below, but I would like it to work for any configuration of the below grid. So all I have to do is give the current coordinates of the * and it will give me the number of possible positions it can move to.
A thing to note is that the *'s original position is not considered in the calculations, which is why for the example below the message would print 9 and not 10.
I have a isaWall method that tells me if the cell is a wall or not. The isaWall method is in a Cell class. Each cell is represented by its coordinates. I looked into using Algorithms like BFS or DFS, but I didn't quite understand how to implement them in this case, as I am not too familiar with the algorithms. I thought of using the Cells as nodes of the graph, but wasn't too sure how to traverse the graph because from the examples I saw online of BFS and DFS, you would usually have a destination node and source node (the source being the position of the *), but I don't really have a destination node in this case. I would really appreciate some help.
00111110
01000010
100*1100
10001000
11111000
EDIT: I checked the website that was recommend in the comments and tried to implement my own version. It unfortunately didn't work. I understand that I have to expand the "frontier" and I basically just translated the expansion code to Java, but it still doesn't work. The website continues explaining the process, but in my case, there is no destination cell to go to. I'd really appreciate an example or a clearer explanation pertaining to my case.
EDIT2: I'm still quite confused by it, can someone please help?
While BFS/DFS are commonly used to find connections between a start and end point, that isn't really what they are. BFS/DFS are "graph traversal algorithms," which is a fancy way of saying that they find every point reachable from a start point. DFS (Depth First Search) is easier to implement, so we'll use that for your needs (note: BFS is used when you need to find how far away any point is from the start point, and DFS is used when you only need to go to every point).
I don't know exactly how your data is structured, but I'll assume your map is an array of integers and define some basic functionality (for simplicity's sake I made the start cell 2):
Map.java
import java.awt.*;
public class Map {
public final int width;
public final int height;
private final Cell[][] cells;
private final Move[] moves;
private Point startPoint;
public Map(int[][] mapData) {
this.width = mapData[0].length;
this.height = mapData.length;
cells = new Cell[height][width];
// define valid movements
moves = new Move[]{
new Move(1, 0),
new Move(-1, 0),
new Move(0, 1),
new Move(0, -1)
};
generateCells(mapData);
}
public Point getStartPoint() {
return startPoint;
}
public void setStartPoint(Point p) {
if (!isValidLocation(p)) throw new IllegalArgumentException("Invalid point");
startPoint.setLocation(p);
}
public Cell getStartCell() {
return getCellAtPoint(getStartPoint());
}
public Cell getCellAtPoint(Point p) {
if (!isValidLocation(p)) throw new IllegalArgumentException("Invalid point");
return cells[p.y][p.x];
}
private void generateCells(int[][] mapData) {
boolean foundStart = false;
for (int i = 0; i < mapData.length; i++) {
for (int j = 0; j < mapData[i].length; j++) {
/*
0 = empty space
1 = wall
2 = starting point
*/
if (mapData[i][j] == 2) {
if (foundStart) throw new IllegalArgumentException("Cannot have more than one start position");
foundStart = true;
startPoint = new Point(j, i);
} else if (mapData[i][j] != 0 && mapData[i][j] != 1) {
throw new IllegalArgumentException("Map input data must contain only 0, 1, 2");
}
cells[i][j] = new Cell(j, i, mapData[i][j] == 1);
}
}
if (!foundStart) throw new IllegalArgumentException("No start point in map data");
// Add all cells adjacencies based on up, down, left, right movement
generateAdj();
}
private void generateAdj() {
for (int i = 0; i < cells.length; i++) {
for (int j = 0; j < cells[i].length; j++) {
for (Move move : moves) {
Point p2 = new Point(j + move.getX(), i + move.getY());
if (isValidLocation(p2)) {
cells[i][j].addAdjCell(cells[p2.y][p2.x]);
}
}
}
}
}
private boolean isValidLocation(Point p) {
if (p == null) throw new IllegalArgumentException("Point cannot be null");
return (p.x >= 0 && p.y >= 0) && (p.y < cells.length && p.x < cells[p.y].length);
}
private class Move {
private int x;
private int y;
public Move(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
}
Cell.java
import java.util.LinkedList;
public class Cell {
public final int x;
public final int y;
public final boolean isWall;
private final LinkedList<Cell> adjCells;
public Cell(int x, int y, boolean isWall) {
if (x < 0 || y < 0) throw new IllegalArgumentException("x, y must be greater than 0");
this.x = x;
this.y = y;
this.isWall = isWall;
adjCells = new LinkedList<>();
}
public void addAdjCell(Cell c) {
if (c == null) throw new IllegalArgumentException("Cell cannot be null");
adjCells.add(c);
}
public LinkedList<Cell> getAdjCells() {
return adjCells;
}
}
Now to write our DFS function. A DFS recursively touches every reachable cell once with the following steps:
Mark current cell as visited
Loop through each adjacent cell
If the cell has not already been visited, DFS that cell, and add the number of cells adjacent to that cell to the current tally
Return the number of cells adjacent to the current cell + 1
You can see a visualization of this here. With all the helper functionality we wrote already, this is pretty simple:
MapHelper.java
class MapHelper {
public static int countReachableCells(Map map) {
if (map == null) throw new IllegalArgumentException("Arguments cannot be null");
boolean[][] visited = new boolean[map.height][map.width];
// subtract one to exclude starting point
return dfs(map.getStartCell(), visited) - 1;
}
private static int dfs(Cell currentCell, boolean[][] visited) {
visited[currentCell.y][currentCell.x] = true;
int touchedCells = 0;
for (Cell adjCell : currentCell.getAdjCells()) {
if (!adjCell.isWall && !visited[adjCell.y][adjCell.x]) {
touchedCells += dfs(adjCell, visited);
}
}
return ++touchedCells;
}
}
And that's it! Let me know if you need any explanations about the code.

How to count a cell's neighbors in a cellular automaton with wraparound

So I'm making a program that simulates Life-like cellular automata, but I'm having some trouble with the method used to count a cell's live neighbors. The problem is that I want to be able to change how the grid wraps around -- that is, whether it wraps around from left to right (i.e., cylindrical), from top to bottom and left to right (i.e., toroidal), or not at all (i.e., flat) -- and I can't figure out how to make my method account for that. Here's what I have so far:
public int getLiveNeighbors(int row, int col)
{
int count = 0;
// "topology" is an int that represents wraparound:
// 0 = flat; 1 = cylindrical; 2 = toroidal
int top = topology != 2 ? row - 1 : (row + ROWS - 1) % ROWS;
int bottom = topology != 2 ? row + 1 : (row + 1) % ROWS;
int left = topology != 0 ? (col + COLS - 1) % COLS : col - 1;
int right = topology != 0 ? (col + 1) % COLS : col + 1;
for (int r = top; r < bottom + 1; r++)
for (int c = left; c < right + 1; c++)
if (!(r == row && c == col) && getCell(r, c).equals(LIVE))
count++;
}
The key, I think, is the if-statement in the for-loop -- there has to be some way to check whether r and c are within the bounds of the grid, while keeping in mind that the definition of "bounds" will vary depending on whether/how the grid wraps around. In the past I've gotten around this by having three different sets (one for each wraparound setting) of eight different if-statements to individually check each of the eight cells comprising the original cell's neighborhood; as you can imagine, it was not very pretty, but at least it worked.
I'm not so great at explaining my own code, so I hope that wasn't too confusing -- I'm feeling a little loopy myself (ha). If anyone has any questions, feel free to ask!
You probably already have a class like Board with a method like getCell(x, y) (at least a method of this kind is present in your code).
I'd just make this method lenient in a sense that it would accept negative x and y or x and y greater or equal to COLS and ROWS. Thus you could just iterate over col - 1 to col + 1 and row - 1 to row + 1 (minus col and row) and not care that these coordinates go "over the board". It's the task of the Board to do coordinate lookups correctly.
What makes your code harder is also that you handle different topologies in one place. It's quite hard to follow.
You could make it simpler by implementing different subclasses of Board like CylindricalBoard, ToroidalBoard and FlatBoard. Each of the subclasses would implement getCell differently, but in the context of the subclass it will be clearly understandable.
You're looking for the Strategy Pattern:
There are common situations when classes differ only in their behavior. For this cases is a good idea to isolate the algorithms in separate classes in order to have the ability to select different algorithms at runtime.
In this case you'd want something like this (abbreviated for clarity):
class Point {
int x;
int y;
}
interface WrapStrategy {
Point moveUp(Point p);
Point moveDown(Point p);
Point moveLeft(Point p);
Point moveRight(Point p);
}
class CylinderWrapping implements WrapStrategy {
int height;
int circumference;
Point moveUp(Point p) {
if (p.y <= 0)
return null; // cannot move up
return new Point(p.x, p.y - 1);
}
Point moveDown(Point p) {
if (p.y >= height - 1)
return null; // cannot move down
return new Point(p.x, p.y + 1);
}
Point moveLeft(Point p) {
if (p.x <= 0)
return new Point(circumference - 1, p.y);
return new Point(p.x - 1, p.y);
}
Point moveRight(Point p) {
if (p.x >= circumference - 1)
return new Point(0, p.y);
return new Point(p.x + 1, p.y);
}
}
Try this:
import java.awt.Point;
public class Neighbours {
public static void main(String[] args) {
Neighbours inst=new Neighbours();
int r=3;//<ROWS
int c=3;//<COLS
for(int i :new int[]{0,1,2}){
inst.type=i;
System.out.format("There are %d neighbours of point (%d,%d), topography type %d\n", inst.countLiveNeighbours(r, c), c, r,i);
}
}
int ROWS=4;
int COLS=4;
int type=0;//0=flat, 1=cylinder, 2=toroid
/**
* Is x,y a neighbour of r,c?
* #return coordinates of neighbour or null
*/
Point neighbour(int x, int y, int r, int c){
if((x==c)&&(y==r))
return null;
switch (type){
/*this is wrong for the reasons explained below
case 0: return ((x<COLS)&&(y<ROWS)) ? new Point (x,y) : null;
case 1: return y<ROWS ? new Point(x%COLS,y) : null;
case 2: return new Point(x%COLS,y%ROWS);
*/
//replacement statements produce the correct behaviour
case 0: return ((x<COLS)&&(x>-1)&&(y<ROWS)&&(y>-1)) ? new Point (x,y) : null;
case 1: return ((y<ROWS)&&(y>-1)) ? new Point(Math.floorMod(x,COLS),y) : null;
case 2: return new Point(Math.floorMod(x,COLS),Math.floorMod(y,ROWS));
}
return null;
}
int countLiveNeighbours(int r, int c){
int result=0;
for(int x=c-1; x<c+2; x++)
for(int y=r-1; y<r+2; y++){
Point p=neighbour(x,y,r,c);
if(live(p)){
System.out.format("\tpoint (%d,%d)\n",(int)p.getX(),(int)p.getY());
result++;
}
}
return result;
}
boolean live(Point p){
boolean result=true;
if(p==null)
return false;
//perform tests for liveness here and set result
return result;
}
}

How to iterate over ordinal directions over a point (x, y)?

I have this function below called findNeighboringChains(i, j). You pass in a point (x, y) and it returns all chain objects that exist in the neighboring points (x + 1, y), (x - 1, y), (x, y + 1), and (x, y - 1). If no chain exists at (x, y) then findChainId(x, y) = -1, else it will return an ID >= 0. Only one chain can exist at each (x, y). For more context, this is a function I am using to find adjacent chains to a cell in the game of Go.
I feel like what I have so far is kind of verbose, but I'm not sure how to make it better. It seems like it would be ideal if I could iterate over those points (x + 1, y) ... (x, y - 1) through a loop. Any suggestions?
public ArrayList<Integer> findNeighboringChains(int i, int j) {
ArrayList<Integer> neighboringChains = new ArrayList<>();
int tmp = findChainId(i - 1, j);
if (tmp != -1) {
neighboringChains.add(tmp);
}
tmp = findChainId(i + 1, j);
if (tmp != -1) {
neighboringChains.add(tmp);
}
tmp = findChainId(i, j - 1);
if (tmp != -1) {
neighboringChains.add(tmp);
}
tmp = findChainId(i, j + 1);
if (tmp != -1) {
neighboringChains.add(tmp);
}
return neighboringChains;
}
One way would be to utilize the Point object that is built into Java, and iterate over a list of points- each time calling the same piece of code. In my solution/refactoring, I create a new method called "getNeighboringPoints(Point p)", which retrieves the four neighboring points. Then, in your function findNeighboringChains, you can iterate using a for-each loop over that list of points.
There are a lot of variations on this kind of pattern you could do, but you're definitely right to think that it's possible to be less redundant. It's always a good idea to try to follow the DRY principle.
public ArrayList<Integer> findNeighboringChains(int i, int j) {
ArrayList<Integer> neighboringChains = new ArrayList<>();
Point p = new Point(i, j);
List<Point> neighboringPoints = getNeighboringPoints(p);
for (Point point : neighboringPoints) {
int tmp = findChainId(point.x, point.y);
if (tmp != -1) {
neighboringChains.add(tmp);
}
}
return neighboringChains;
}
/**
*
* #param p
* The input point.
* #return a list of points neighboring point p
*/
private List<Point> getNeighboringPoints(Point p) {
ArrayList<Point> neighboringPoints = new ArrayList<Point>();
neighboringPoints.add(new Point(p.x - 1, p.y));
neighboringPoints.add(new Point(p.x + 1, p.y));
neighboringPoints.add(new Point(p.x, p.y + 1));
neighboringPoints.add(new Point(p.x, p.y - 1));
return neighboringPoints;
}
One of the benefits of the above method, is now you can later on find that you might need to do another operation on all the neighboring points, and you can reuse the method getNeighboringPoints().
Edit:
Another way to reduce redundancy would be to use the extract method technique.
public ArrayList<Integer> findNeighboringChains(int i, int j) {
ArrayList<Integer> neighboringChains = new ArrayList<>();
int tmp = findChainId(i - 1, j);
checkChain(neighboringChains, tmp);
tmp = findChainId(i + 1, j);
checkChain(neighboringChains, tmp);
tmp = findChainId(i, j - 1);
checkChain(neighboringChains, tmp);
tmp = findChainId(i, j + 1);
checkChain(neighboringChains, tmp);
return neighboringChains;
}
private void checkChain(ArrayList<Integer> neighboringChains, int tmp) {
if (tmp != -1) {
neighboringChains.add(tmp);
}
}
This might be better since it doesn't force usage of the point class on a project which already isn't using points. (It can be annoying when there is one method that uses points where everything else requires the input of two ints).

ArrayList optimization

i am currently working on this for personal gratification and would like some advice on how i can make this code faster :
I have one ArrayList composed of an object note, which have coordinates and color value stored in it.
Each "note" is created in real time during the rendering call.
I have made this function :
void keyPressed() {
if (key == 's' || key == 'S') {
PImage img = createImage(posX, specSize, RGB);
for(int x = 0; x < posX; x++){
for(int y = 0; y < specSize; y++){
for(int i = 0; i < notes.size(); i++){
if( (notes.get(i).getX() == x)
&& (notes.get(i).getY() == y) ){
int loc = x + y*posX;
img.pixels[loc] = color(notes.get(i).getR(),
notes.get(i).getG(), notes.get(i).getB());
}
}
}
}
img.updatePixels();
img.save("outputImage.png");
}
}
So when i press the "S" key, i run a loop on the width and height because they can be different in each run, and then on my arrayList and get the corresponding "note" with it's x and y position.
then i write my picture file.
As you can imagine, this is really, really, slow...
Around 5 to 6 minutes for a 1976x256px file.
For me it's okay but it would be great to shorten this a little.
Is there a way to optimize this three loops?
If you need more code, please let me know it's a small code and i don't mind.
How about this?
void keyPressed() {
if (key == 's' || key == 'S') {
PImage img = createImage(posX, specSize, RGB);
for(int i = 0; i < notes.size(); i++){
int x = notes.get(i).getX();
int y = notes.get(i).getY();
int loc = x + y*posX;
img.pixels[loc] = color(notes.get(i).getR(),
notes.get(i).getG(), notes.get(i).getB());
}
img.updatePixels();
img.save("outputImage.png");
}
}
Update:
Not sure what the type of notes is, but something like this might work too. Insert the correct type for one element of Notes into the for loop where I wrote ???.
void keyPressed() {
if (key == 's' || key == 'S') {
PImage img = createImage(posX, specSize, RGB);
for(??? note : notes ){
int x = note.getX();
int y = note.getY();
int loc = x + y * posX;
img.pixels[loc] = color(note.getR(), note.getG(), note.getB());
}
img.updatePixels();
img.save("outputImage.png");
}
}
Can clone notes (and any other object that is used to save) and do this in a different thread so its async to UI. the code will take same or more time but the user can use the rest of the app. Clone is neccesary as you want a snap shot of state when save was clicked.
Dont make a thread put use a ThreadPoolExecutor with one thread max. In the run method could apply what David suggested - one loop instead of two.
Convert your list of notes into a structure mapped like
Map<Integer, Map<Integer, Note> noteMap
Then replace your inner-most loop with a single call like
yNoteMap = note.get(x);
if (yNoteMap != null) {
note = yNoteMap.get(y);
if (note != null) {
// do stuff with note
}
}
Your computational complexity will go from about O(n^3) to O(n^2).
Create a class such as Point with two properties of x and y and implement proper equals and hashcode methods as:
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Point point = (Point) o;
if (x != point.x)
return false;
if (y != point.y)
return false;
return true;
}
#Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
}
now put the Point as key of a map, and find your points using this, so you don't have to iterate over the whole lists.

Categories