String Pathing BFS - java

I am trying to find the shortest path between two strings, and return an int of how many steps were taken. Given that I have a HashMap in which each String(key) has a String[](object) containing all of that strings neighbours.
This code is what I whipped up. I just took a basic BFS and tried to copy that, but I can't quite figure out a way to progress.
public class Main {
private static HashMap<String, String[]> list;
private static int makePath(String from, string to) {
int path = 0;
PriorityQueue<String> queue = new PriorityQueue<>();
queue.add(from);
while (!queue.isEmpty()) {
String u = queue.poll();
if (u == to) {
return path;
}
else {
for (String r : list.get(u)) {
...
}
return path;
}
}
return 0;
}
}
This is just an example of what my HashMap might look like:
Goat, adj[] {Fish, Cow, Chicken}
Cow, adj[] {Pig, Pigeon}
Fish, adj[] {Goat, Bulbasaur, Dolphin, Eagle}
From Fish to Cow I need two steps. From Fish to Goat and Goat to Fish.
So if you got any ideas feel free to share :)

I am thinking of using 2 Queues. I will enqueue the from word to the firstQueue, and while firstQueue is not empty, I will perform an alternating logic to store the neighbors in the other queue if that neighbor is still not equal to to.
It'll be clearer if I give the code,
private static int makePath(final HashMap<String, String[]> tree, final String from, final String to) {
int path = 0;
Queue<String> firstQueue = new PriorityQueue<>();
Queue<String> secondQueue = new PriorityQueue<>();
firstQueue.add(from);
while (!firstQueue.isEmpty()) {
String key = firstQueue.poll();
String[] neighbors = tree.get(key);
if (neighbors != null) {
path++;
for (String neighbor : neighbors) {
if (neighbor.equals(to)) {
return path;
} else {
secondQueue.add(neighbor);
}
}
}
while (!secondQueue.isEmpty()) {
key = secondQueue.poll();
neighbors = tree.get(key);
if (neighbors != null) {
path++;
for (String neighbor : neighbors) {
if (neighbor.equals(to)) {
return path;
} else {
firstQueue.add(neighbor);
}
}
}
}
}
return 0;
}

Related

Follow list path with recursion. [Java]

Hello i have a list with different connections. I want a function that allows me to give a start and end value. The function needs to figure out if there is a path. And if there's a path, it needs to be returned. I already created a working program. But the only thing is that i only get if there is a path yes or no. But i want to get the path.
private static String[] connections = {
"Strawberry -> Apple",
"Apple -> Strawberry",
"Strawberry -> Melon",
"Melon -> Strawberry"
};
public static void main(String[] args) {
System.out.println(testConnection("Apple", "Melon", new ArrayList<>()));
}
public static boolean testConnection(String from, String to, List<String> checked) {
if(from.equals(to)) return true;
for(String con : connections) {
String[] conSplit = con.split(" -> ");
String first = conSplit[0];
String second = conSplit[1];
if(first.equals(from)) {
if(second.equals(to)) return true;
if(checked.contains(second)) continue;
checked.add(first);
if(testConnection(second, to, checked)) return true;
}
}
return false;
}
As you see Apple goes to Strawberry, Strawberry goes to Melon.
And in my testConnection I said that i need to get the path from Apple to Melon.
You can use many ways to do that.
In these cases I don't like to use recursion, but it's up to you.
For example you can just use the print statement in your loop to print first and second strings when the statement if(first.equals(from)) returns true.
Here is my version of the testConnection method using iteration:
public static boolean testConnection(String from, String to, List<String> checked) {
boolean flag = false;
for (String con : connections) {
String[] conSplit = con.split(" -> ");
String first = conSplit[0];
String second = conSplit[1];
if (first.equals(from)) {
checked.add(first);
checked.add(second);
from = second;
if (second.equals(to)) {
flag = true;
break;
}
}
}
if (flag)
System.out.println(checked);
return flag;
}
Looking at the checked list is not sufficient to construct the path, since it will only be the same as the path if no backtracking is used. For example, with the connections:
private static String[] connections = {
"Strawberry -> Apple",
"Apple -> Pineapple",
"Apple -> Strawberry",
"Strawberry -> Cherry",
"Cherry -> Apple",
"Strawberry -> Melon",
"Melon -> Strawberry",
};
You would end up checking Apple, Pineapple, Strawberry, Cherry then Melon, even though the path is Apple, Strawberry, Melon. A separate list is needed to find the path, e.g.:
public static boolean testConnection(String from, String to, List<String> checked, Stack<String> path) {
checked.add(from);
path.add(from);
if (from.equals(to)) return true;
for (String con : connections) {
String[] conSplit = con.split(" -> ");
String first = conSplit[0];
String second = conSplit[1];
if (first.equals(from)) {
if (second.equals(to)) {
path.add(second);
return true;
}
if (checked.contains(second)) continue;
if (testConnection(second, to, checked, path)) return true;
}
}
path.pop();
return false;
}
The key is that if all the connections are checked and no path is found, then the item is removed from the end of the path (path.pop()). This means that if a path is explored and then it is found that it doesn't lead to the destination, it won't affect the final path.
This algorithm is quite inefficient however, since every iteration it loops over every single connection. This can be fixed by building a graph data structure:
class Node {
String name;
List<Node> nextNodes;
public Node(String name) {
this.name = name;
this.nextNodes = new ArrayList<>();
}
public static Map<String, Node> fromConnections(String[] connections) {
Map<String, Node> nodes = new HashMap<>();
for (String con :
connections) {
String[] conSplit = con.split(" -> ");
String first = conSplit[0];
String second = conSplit[1];
Node node = nodes.computeIfAbsent(first, Node::new);
node.nextNodes.add(nodes.computeIfAbsent(second, Node::new));
}
return nodes;
}
}
This only loops over the entire list once. Now finding the connections that lead from a node only involves looping over those connections, not the entire list. It also separates the parsing stage from the search stage. Lastly, when you are going to do many checked.contains(second) checks, it is more efficient to use a HashSet rather than a list. Checking if a list contains an element involves looping over the entire list, whereas checking if a hashset contains something takes the same amount of time regardless of the size of the hashset.
Here is a running example of both versions of the algorithm:
import java.util.*;
public class Main {
static class Node {
String name;
List<Node> nextNodes;
public Node(String name) {
this.name = name;
this.nextNodes = new ArrayList<>();
}
public static Map<String, Node> fromConnections(String[] connections) {
Map<String, Node> nodes = new HashMap<>();
for (String con :
connections) {
String[] conSplit = con.split(" -> ");
String first = conSplit[0];
String second = conSplit[1];
Node node = nodes.computeIfAbsent(first, Node::new);
node.nextNodes.add(nodes.computeIfAbsent(second, Node::new));
}
return nodes;
}
}
private static String[] connections = {
"Strawberry -> Apple",
"Apple -> Pineapple",
"Apple -> Strawberry",
"Strawberry -> Cherry",
"Cherry -> Apple",
"Strawberry -> Melon",
"Melon -> Strawberry",
};
public static void main(String[] args) {
Stack<String> path = new Stack<>();
ArrayList<String> checked = new ArrayList<>();
System.out.println(testConnection("Apple", "Melon", checked, path));
System.out.println(path);
System.out.printf("checked: %s\n", checked);
Map<String, Node> graph = Node.fromConnections(connections);
path = new Stack<>();
System.out.println(testConnection(graph.get("Apple"), graph.get("Melon"), new HashSet<>(), path));
System.out.println(path);
}
public static boolean testConnection(String from, String to, List<String> checked, Stack<String> path) {
checked.add(from);
path.add(from);
if (from.equals(to)) return true;
for (String con : connections) {
String[] conSplit = con.split(" -> ");
String first = conSplit[0];
String second = conSplit[1];
if (first.equals(from)) {
if (second.equals(to)) {
path.add(second);
return true;
}
if (checked.contains(second)) continue;
if (testConnection(second, to, checked, path)) return true;
}
}
path.pop();
return false;
}
public static boolean testConnection(Node from, Node to, Set<Node> checked, Stack<String> path) {
checked.add(from);
path.add(from.name);
if (from == to) return true;
for (Node next : from.nextNodes) {
if (checked.contains(next)) continue;
if (testConnection(next, to, checked, path)) return true;
}
path.pop();
return false;
}
}

2-D array Friends of Friends List

2*2 Matrix
Men Friends
A B,C,D
B E,F
C A
E B
F B
G F
I need list of Friends and Friends of Friends for requested men.
Example Like G -> F,B,E,F,B and After removing duplicate F,B,E
I resolved it with loops and recursion but not satisfied
Need better approach/suggestion.. rest i will implement.
Why not try something like this. Of course, i have taken a bit of freedom on the design as you didnt provide any code. Hope this helps!
private static Set<Node> getNodesFollowAllEdges(Node node) {
Set<Node> nodes = getNodesFollowAllEdges(node, new HashSet<>());
// remember to remove the original node from the set
nodes.remove(node);
return nodes;
}
private static Set<Node> getNodesFollowAllEdges(Node node, Set<Node> visited) {
if (node.getConnectedNodes().isEmpty()) {
return visited;
}
for (Node n : node.getConnectedNodes()) {
if (!visited.contains(n)) {
visited.add(n);
getNodesFollowAllEdges(n, visited);
}
}
return visited;
}
Also, it is very easy to provide a maximum search dept. Just add int maxDept and increase it every recursion step.
Given the following example:
Node a = new Node("A");
Node b = new Node("B");
Node c = new Node("C");
Node d = new Node("D");
Node e = new Node("E");
Node f = new Node("F");
Node g = new Node("G");
a.addConnectedNodes(b, c, d);
b.addConnectedNodes(e, f);
c.addConnectedNodes(a);
e.addConnectedNodes(b);
f.addConnectedNodes(b);
g.addConnectedNodes(f);
Set<Node> friends = getNodesFollowAllEdges(a);
friends.forEach(node -> System.out.println(node.getName()));
should give you the correct result of (order neglected)
B
F
E
Note: Remember that, since its a Set, the resulting nodes can be in any order.
Thanks Glains,
My Code is :
public class Demo {
Set<String> freinds = new HashSet<>();
public static void main(String[] args) {
String[][] emailArray = new String[][] {
{ "abc#gmail.com", "abc1#gmail.com,abc2#gmail.com,abc3#gmail.com,1212#gmail.com" },
{ "abc1#gmail.com", "bcs1#gmail.combc2#gmail.com,bcds3#gmail.com" },
{ "bc#gmail.com", "bc1#gmail.combc2#gmail.com,bc3#gmail.com" } };
new Demo().sendMail("#gmail.combc2#gmail.com", "sdsd", emailArray);
}
void sendMail(String email, String message, String[][] freindsArray) {
Map<String, Email> emailsMap = new HashMap<>();
for (int i = 0; i < freindsArray.length; i++) {
for (int j = 0; j < 1; j++) {
Email e = new Email(freindsArray[i][j]);
e.addConnectedNodes(freindsArray[i][j + 1]);
emailsMap.put(e.email, e);
}
}
if (emailsMap.containsKey(email)) {
Demo.getNodesFollowAllEdges(emailsMap.get(email), emailsMap).forEach(e -> {
System.out.println(e);
});
} else {
System.out.println("no emails exist");
}
}
private static Set<String> getNodesFollowAllEdges(Email e, Map<String, Email> emailsMap) {
Set<String> nodes = getNodesFollowAllEdges(e, new HashSet<>(), emailsMap);
nodes.remove(e.email);
return nodes;
}
private static Set<String> getNodesFollowAllEdges(Email node, Set<String> visited, Map<String, Email> emailsMap) {
if (node.getConnectedEmails().isEmpty()) {
return visited;
}
for (String n : node.getConnectedEmails()) {
if (!visited.contains(n)) {
visited.add(n);
if (emailsMap.get(n) != null) {
getNodesFollowAllEdges(emailsMap.get(n), visited, emailsMap);
}
}
}
return visited;
}
}
class Email {
String email;
List<String> freindsEmails = new ArrayList<>();
public List<String> getConnectedEmails() {
return freindsEmails;
}
public Email(String email) {
this.email = email;
}
public void addConnectedNodes(String friendsEmail) {
freindsEmails.addAll(Arrays.asList(friendsEmail.split(",")));
}
}

StackOverflowError while running Depth First Search (undirected graph)

I have a DFS visit recursive method that sometimes throws a StackOverflowError. Since the size of the graph is large (around 20000 vertices), recursive calls are many, and so I tried to run with -Xss10M and everything works.
I'd just like to understand why adding at the beginning of the method a System.out.println, even without -Xss10M, the method doesn't throw any StackOverflowError. How is it possible?
This is the DFS visit method:
private int dfsVisit(Vertex<T> v, int time){
// System.out.println("Hello");
Vertex<T> n;
time++;
v.d = time;
v.color = Vertex.Color.GRAY;
for (Map.Entry<Vertex<T>, Float> a : v.neighbours.entrySet()){
n = a.getKey();
if(n.color == Vertex.Color.WHITE){
n.previous = v;
time = dfsVisit(n, time);
}
}
v.color = Vertex.Color.BLACK;
time++;
v.f = time;
return time;
}
This is the complete code
import java.io.*;
import java.util.*;
class Graph<T> {
private final Map<T, Vertex<T>> graph;
public static class Edge<T>{
public final T v1, v2;
public final float dist;
public Edge(T v1, T v2, float dist) {
this.v1 = v1;
this.v2 = v2;
this.dist = dist;
}
}
public static class Vertex<T> implements Comparable<Vertex>{ // SPOSTARE VAR IST NEL COSTRUTTORE
public enum Color {WHITE, GRAY, BLACK, UNKNOWN};
public final T name;
public float dist;
public Vertex<T> previous;
public final Map<Vertex<T>, Float> neighbours;
public Color color;
public int d, f;
public Vertex(T name) {
this.name = name;
dist = Float.MAX_VALUE;
previous = null;
neighbours = new HashMap<Vertex<T>, Float>(); // adjacency list
color = Color.UNKNOWN;
d = 0;
f = 0;
}
private void printPath() {
if (this == this.previous) {
System.out.print(this.name);
} else if (this.previous == null) {
System.out.print(this.name + " unreached");
} else {
this.previous.printPath();
System.out.print(" -> " + this.name + "(" + this.dist + ")");
}
}
public int compareTo(Vertex other){
if(this.dist == other.dist)
return 0;
else if(this.dist > other.dist)
return 1;
else
return -1;
}
}
// Builds a graph from an array of edges
public Graph(ArrayList<Graph.Edge> edges) {
graph = new HashMap<>(edges.size());
// add vertices
for (Edge<T> e : edges) {
if (!graph.containsKey(e.v1)) graph.put(e.v1, new Vertex<>(e.v1));
if (!graph.containsKey(e.v2)) graph.put(e.v2, new Vertex<>(e.v2));
}
// create adjacency list
for (Edge<T> e : edges) {
graph.get(e.v1).neighbours.put(graph.get(e.v2), e.dist);
graph.get(e.v2).neighbours.put(graph.get(e.v1), e.dist);
}
}
public void dijkstra(T startName) {
if (!graph.containsKey(startName)) {
System.err.println("Graph doesn't contain start vertex " + startName);
return;
}
final Vertex<T> source = graph.get(startName);
NavigableSet<Vertex<T>> q = new TreeSet<>(); // priority queue
// set-up vertices
for (Vertex<T> v : graph.values()) {
v.previous = v == source ? source : null;
v.dist = v == source ? 0 : Float.MAX_VALUE;
q.add(v);
}
dijkstra(q);
}
private void dijkstra(final NavigableSet<Vertex<T>> q) {
Vertex<T> u, v;
while (!q.isEmpty()) {
u = q.pollFirst();
if (u.dist == Float.MAX_VALUE) break; //???????????
for (Map.Entry<Vertex<T>, Float> a : u.neighbours.entrySet()) {
v = a.getKey();
final float alternateDist = u.dist + a.getValue();
if (alternateDist < v.dist) {
q.remove(v);
v.dist = alternateDist;
v.previous = u;
q.add(v);
}
}
}
}
public void printPath(T endName) {
if (!graph.containsKey(endName)) {
System.err.println("Graph doesn't contain end vertex " + "\"" + endName + "\"" );
return;
}
graph.get(endName).printPath();
System.out.println();
}
public void printAllPaths() {
for (Vertex<T> v : graph.values()) {
v.printPath();
System.out.println();
}
}
public Vertex<T> getVertex(T key){
if(graph.containsKey(key))
return graph.get(key);
return null;
}
public void printAdjacencyList(){
System.out.println("Adjacency list:");
for(Vertex<T> v : graph.values()){
System.out.print(v.name + ":\t");
for (Map.Entry<Vertex<T>, Float> a : v.neighbours.entrySet()){
System.out.print(a.getKey().name + "(" + a.getValue() + ") | ");
}
System.out.println();
}
}
/*
P.S. I know that if only used to calculate the connected components of the graph, dfs visit
could be written differently but I preferred to write it in a more general way, so that it
can be reused if necessary.
*/
private int dfsVisit(Vertex<T> v, int time){
// System.out.println("ciao");
Vertex<T> n;
time++;
v.d = time;
v.color = Vertex.Color.GRAY;
for (Map.Entry<Vertex<T>, Float> a : v.neighbours.entrySet()){
n = a.getKey();
if(n.color == Vertex.Color.WHITE){
n.previous = v;
time = dfsVisit(n, time);
}
}
v.color = Vertex.Color.BLACK;
time++;
v.f = time;
return time;
}
/*
Print the size of the connected components of the graph
*/
public void connectedComponents(){
for(Vertex<T> v : graph.values()){
v.color = Vertex.Color.WHITE;
v.previous = null;
}
for(Vertex<T> v : graph.values()){
if(v.color == Vertex.Color.WHITE)
System.out.println(dfsVisit(v, 0)/2);
}
}
}
here's the test class
import java.io.*;
import java.util.*;
public class Dijkstra {
private static ArrayList<Graph.Edge> a = new ArrayList<Graph.Edge>();
private static final String START = "torino";
private static final String END = "catania";
public static void main(String[] args) {
String fileName = "italian_dist_graph.txt";
try{
Scanner inputStream = new Scanner(new File(fileName));
String record;
while(inputStream.hasNextLine()){
record = inputStream.nextLine();
String[] array = record.split(",");
String from = array[0];
String to = array[1];
float dist = Float.parseFloat(array[2]);
a.add(new Graph.Edge(from, to, dist));
}
inputStream.close();
} catch(FileNotFoundException e){
System.out.println("Impossibile trovare il file "+fileName);
}
Graph<String> g = new Graph<String>(a);
g.dijkstra(START);
g.printPath(END);
//System.out.printf("%f\n", g.getVertex(END).dist/1000.0f);
g.connectedComponents();
}
}
N.B. try to comment g.dijkstra(START) and g.printPath(END); everything seems to work.
Here's the link to the data set
https://drive.google.com/open?id=0B7XZY8cd0L_fZVl1aERlRmhQN0k
Some general recommendations:
Your code mixes up attributes of vertices, that are related to a single run of dfs and such that are direct attributes of the vertices. Bad bad bad style. This is quite likely to break any more complex algorithm, can produce unexpected behavior and would require clearing the states after each run, to ensure stability of the code. Instead keep states that are related to a single run of a algorithm only visible to that function. E.g. store the states inside a Map, use the decorator-pattern to create a datastructure that provides additional attributes and that has method-local scope, etc.. As an example: running your code twice on the same graph (same Object) with the same input without clearing all states will lead to a wrong result (1).
In addition: creating an iterative version of DFS isn't exactly hard, so you should give it a try, especially since your graph appears to be pretty large.
As for why your code works (or doesn't) the way it does:
This is hard to tell, since it depends upon quite a lot of factors. You didn't provide full code, so I can't rerun any tests, or verify that everything behaves the way it should. The most likely answers:
Vertex uses the default hash-code provided by Object. This leads to random ordering of the entries in the map of neighbours, thus the order in which specific paths are traversed is random in each run and most likely different. Thus you're traversing the graph using random paths, that quite likely (especially due to the size of your graph) differ for each run. The reason isn't the System.out.println, but the mere fact, that your code generates a different structure (from a ordering-POV, not mathematical), each time it runs plus the coincident, that for some pretty weird reason each build of the graph, that doesn't reach the necessary recursion-depth for a StackOverflow, and the code compiled with System.out.println appeared together.
The Java compiler, or JIT modifies the behavior of the code in a weird way. Modern compilers have the tendency to produce quite weird code in their attempts to optimize everything they can get hold off.

Sorting using comparable

Intro
My code to do a custom sort by using Comparable is not work the way I want it to. I'm basically taking an Array of directories and sorting them by:
First number of directories, the fewer comes first.
If it's a tie alphabetically.
The problem
An example of an input you be:
["/", "/usr/", "/usr/local/", "/usr/local/bin/", "/games/",
"/games/snake/", "/homework/", "/temp/downloads/" ]
Which should return this:
["/", "/games/", "/homework/", "/usr/", "/games/snake/",
"/temp/downloads/", "/usr/local/", "/usr/local/bin/" ]
But for some reason my code is return this:
["/", "/usr/", "/games/", "/homework/", "/usr/local/",
"/games/snake/", "/usr/local/bin/", "/temp/downloads/" ]
My code [edited with comments]
import java.util.*;
public class Dirsort { public String[] sort(String[] dirs) {
//Creates Array list containing Sort object
ArrayList<Sort> mySort = new ArrayList<Sort>();
//Loop that gets the 3 needed values for sorting
for (String d: dirs){
String [] l = d.split("/");//String array for alphabetical comparison
int di = d.length();//Length of array for sorting by number of directories
mySort.add(new Sort(di,l,d));//adds Sort object to arraylist (note d (the entire directory) is needed for the toString)
}
Collections.sort(mySort);//sorts according to compareTo
String [] ans = new String [mySort.size()];//Creates a new string array that will be returned
int count = 0;//to keep track of where we are in the loop for appending
for (Sort s: mySort){
ans[count] = s.toString();
count++;
}
return ans;
}
class Sort implements Comparable<Sort>{
private int d;//number of directories
private String [] arr;//array of strings of names of directories
private String dir;//full directory as string for toString
//Constructor
public Sort(int myD, String [] myArr, String myDir){
d = myD;
arr = myArr;
dir = myDir;
}
//toString
public String toString(){
return dir;
}
#Override
public int compareTo(Sort arg0) {
// TODO Auto-generated method stub
//If they are the same return 0
if (this.equals(arg0)){
return 0;
}
//if the directories are empty
if("/".equals(arg0.dir)){
return 1;
}
if ("/".equals(this.dir)){
return -1;
}
//If they are not the same length the shorter one comes first
if (this.d != arg0.d){
return this.d - arg0.d;
}
//If they are the same length, compare them alphabetically
else{
for (int i = 0; i < arg0.d; i++){
if (!this.arr[i].equals(arg0.arr[i])){
return this.arr[i].compareTo(arg0.arr[i]);
}
}
}
return 0;
}
}
}
The bug is here:
for (String d: dirs){
String [] l = d.split("/");
int di = d.length(); // <- here
mySort.add(new Sort(di,l,d));
}
Because there you are comparing the length of the entire directory String, not the number of 'folders' in the directory. That's why "/usr/" comes before "/homework/", for example, because:
"/usr/".length() == 5
"/homework/".length() == 10
I believe what you wanted was this, using the length of the split:
int di = l.length;
Then the output is:
/
/games/
/homework/
/usr/
/games/snake/
/temp/downloads/
/usr/local/
/usr/local/bin/
There's another small bug though (possibly), which is that calling split on a String that starts with the delimiter will result in an empty String at the beginning.
IE:
"/usr/".split("/") == { "", "usr" }
So you might want to do something about that. Though here it means that all of them start with the empty String so it doesn't end up with an effect on the way you're doing the comparison.
And as a side note, it's also true what #JBNizet is suggesting that giving your variables more meaningful names helps a lot here. fullDir.length() and splitDir.length would have made this much easier to spot (and it may have never happened in the first place).
Here's a fixed version of your code, which handles the case where both directories are "/", which removes the unnecessary, and incorrectly passed length of the parts array, and which uses more meaningful variable names:
public class Dirsort {
public static void main(String[] args) {
String[] input = new String[] {
"/",
"/usr/",
"/usr/local/",
"/usr/local/bin/",
"/games/",
"/games/snake/",
"/homework/",
"/temp/downloads/"
};
String[] result = new Dirsort().sort(input);
System.out.println("result = " + Arrays.toString(result));
}
public String[] sort(String[] dirs) {
ArrayList<Sort> sorts = new ArrayList<Sort>();
for (String dir : dirs) {
String[] parts = dir.split("/");
sorts.add(new Sort(parts, dir));
}
Collections.sort(sorts);
String[] result = new String[sorts.size()];
int count = 0;
for (Sort sort: sorts) {
result[count] = sort.toString();
count++;
}
return result;
}
class Sort implements Comparable<Sort> {
private String[] parts;
private String dir;
public Sort(String[] parts, String dir) {
this.parts = parts;
this.dir = dir;
}
public String toString(){
return dir;
}
#Override
public int compareTo(Sort other) {
if (this.equals(other)){
return 0;
}
if("/".equals(other.dir) && "/".equals(dir)) {
return 0;
}
if("/".equals(other.dir)){
return 1;
}
if ("/".equals(this.dir)){
return -1;
}
if (this.parts.length != other.parts.length){
return this.parts.length - other.parts.length;
}
else {
for (int i = 0; i < other.parts.length; i++){
if (!this.parts[i].equals(other.parts[i])){
return this.parts[i].compareTo(other.parts[i]);
}
}
}
return 0;
}
}
}
I spotted the problem by simply using my debugger and make it display the value of all the variables.
public class Disort
{
public static String[] sort(String[] dirs)
{
ArrayList<Path> mySort = new ArrayList<Path>();
Path pathDir;
for(String dir : dirs){
pathDir = Paths.get(dir);
// check if directory exists
if(Files.isDirectory(pathDir)){
mySort.add(pathDir);
}
}
// sort the ArrayList according a personalized comparator
Collections.sort(mySort, new Comparator<Path>(){
#Override
public int compare(Path o1, Path o2)
{
if(o1.getNameCount() < o2.getNameCount()){
return -1;
}
else if(o1.getNameCount() > o2.getNameCount()){
return 1;
}
else{
return o1.compareTo(o2);
}
}
});
// to return a String[] but it will better to return a ArrayList<Path>
String[] result = new String[mySort.size()];
for(int i = 0; i < result.length; i++){
result[i] = mySort.get(i).toString();
}
return result;
}
}

Java: How to implement Tree structure similar to eclipse package explorer Tree

I have a list of methods stored as a string representation
"com.company.project.service.service1Impl.method()"
"com.company.project.service.service2Impl.method()"
....
with full class/package signature
What would be the most suitable way to implement a tree structure to display the packages/classes/methods in a similar way to eclipse package explorer?
for example:
com
mycompany
myproject1
service
service1Impl
method1
method2
service2impl
controller
controllerImpl
method1
method2
controllerImpl2
myproject2
Note:
not sure if this will make a difference but i m planning to convert this data structure into json to display it in a jquery tree in UI.
Thanks in advance.
I would solve it with a recursive method that has the following arguments:
The array containing the strings
The current prefix
The current depth
The max depth (so it only has to be calculated once)
I think the best way to explain it is with the actual code:
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
Test t = new Test();
String s1 = "com.company.project.service.service1Impl.method()";
String s2 = "com.company.project.service.service2Impl.method()";
String s3 = "com.company.test.service.service1Impl.method()";
String s4 = "com.company.test.service.service2Impl.method()";
String[] strings = { s1, s2, s3, s4 };
t.print(strings);
}
public void print(String[] strings) {
//calculate max depth
int maxDepth = 0;
for (String string : strings) {
int currentDepth = string.split("\\.").length;
if (currentDepth > maxDepth) {
maxDepth = currentDepth;
}
}
this.print(strings, "", 0, maxDepth);
}
public void print(String[] strings, String start, int currentDepth,
int maxDepth) {
if (currentDepth == maxDepth - 1) {
return;
}
String currentPrint = null;
ArrayList<String> candidates = new ArrayList<String>();
// add candidates
for (String s : strings) {
if (!s.startsWith(start)) {
continue;
}
String[] split = s.split("\\.");
if (split.length - 1 < currentDepth) {
continue;
}
if (currentPrint == null) {
currentPrint = split[currentDepth];
candidates.add(currentPrint);
continue;
}
if (!currentPrint.equals(split[currentDepth])) {
currentPrint = split[currentDepth];
candidates.add(currentPrint);
}
}
// print depth+1 with candidates
currentDepth++;
for (String c : candidates) {
// print current level
this.printSpaces(currentDepth - 1);
System.out.println(c);
// we have to go deeper
this.print(strings, start + c + ".", currentDepth, maxDepth);
}
}
// print spaces
public void printSpaces(int max) {
for (int i = 0; i < max; i++) {
System.out.print(" ");
}
}
}
Ask me if you have any questions about the code.
Edit: This of course only works if the list of methods is sorted alphabetically. So if this is not the case, sorting would be the first step.

Categories