5
\$\begingroup\$

Review this code regarding optimization, cleanup, and best practices.

final class EdgePrims<T> {
 private final T source, target;
 private final int distance;
 public EdgePrims(T node1, T node2, int distance) {
 this.source = node1;
 this.target = node2;
 this.distance = distance;
 }
 public T getSource() {
 return source;
 }
 public T getTarget() {
 return target;
 }
 public int getDistance() {
 return distance;
 }
 @Override
 public String toString() {
 return " first vertex " + source + " to vertex " + target + " with distance: " + distance;
 }
}
final class GraphPrims<T> implements Iterable<T> {
 private final Map<T, Map<T, Integer>> graph;
 public GraphPrims() {
 graph = new HashMap<T, Map<T, Integer>>();
 }
 public void addEgde(T vertex1, T vertex2, int distance) {
 if (vertex1 == null) {
 throw new NullPointerException("The vertex 1 cannot be null");
 } 
 if (vertex2 == null) {
 throw new NullPointerException("The vertex 2 cannot be null");
 }
 if (!graph.containsKey(vertex1)) {
 graph.put(vertex1, new HashMap<T, Integer>());
 }
 if (!graph.containsKey(vertex2)) {
 graph.put(vertex2, new HashMap<T, Integer>());
 }
 graph.get(vertex1).put(vertex2, distance);
 graph.get(vertex2).put(vertex1, distance);
 }
 public Set<T> getVertices() {
 return Collections.unmodifiableSet(graph.keySet()); // QQ: should this be replaced by DEEP COPy ?
 }
 public Map<T, Integer> getEdges(T source) {
 if (source == null) {
 throw new NullPointerException("The source cannot be null.");
 }
 return Collections.unmodifiableMap(graph.get(source));
 }
 public void removeEdges(T vertex1, T vertex2) {
 if (vertex1 == null) {
 throw new NullPointerException("The vertex 1 cannot be null");
 } 
 if (vertex2 == null) {
 throw new NullPointerException("The vertex 2 cannot be null");
 }
 if (!graph.containsKey(vertex1)) {
 throw new NoSuchElementException("vertex " + vertex1 + " does not exist.");
 }
 if (!graph.containsKey(vertex2)) {
 throw new NoSuchElementException("vertex " + vertex2 + " does not exist.");
 }
 graph.get(vertex1).remove(vertex2);
 graph.get(vertex2).remove(vertex1);
 }
 @Override
 public Iterator<T> iterator() {
 return graph.keySet().iterator();
 }
}
public class Prims<T> {
 public static Comparator<EdgePrims> edgeComparator = new Comparator<EdgePrims>() {
 @Override
 public int compare(EdgePrims edge1, EdgePrims edge2) {
 return edge1.getDistance() - edge2.getDistance();
 }
 };
 /**
 * Uses prim's algo to calculate a MST for a connected graph.
 * A non-connected graph will lead to unpredictable result.
 * 
 * @param graph a connected graph.
 * @return a list of edges that constitute the MST
 */
 public static <T> List<EdgePrims<T>> getMinSpanTree(GraphPrims<T> graph) {
 Queue<EdgePrims<T>> edgesAvailable = new PriorityQueue<EdgePrims<T>>(10, edgeComparator);
 List<EdgePrims<T>> listMinEdges = new ArrayList<EdgePrims<T>>();
 Set<T> unvisitedVertices = new HashSet<T>();
 unvisitedVertices.addAll(graph.getVertices());
 T sourceVertex = unvisitedVertices.iterator().next();
 unvisitedVertices.remove(sourceVertex);
 while (!unvisitedVertices.isEmpty()) {
 /* populate all edges for the current vertex */
 for (Entry<T, Integer> e : graph.getEdges(sourceVertex).entrySet()) {
 /* dont add a duplicate edge */
 if (unvisitedVertices.contains(e.getKey())) {
 edgesAvailable.add(new EdgePrims(sourceVertex, e.getKey(), (Integer) e.getValue()));
 }
 }
 /* fetch the edge with least distance */
 EdgePrims<T> minEdge = edgesAvailable.poll();
 /* if the target is already visited then move to next edge */
 while (!unvisitedVertices.contains(minEdge.getTarget())) {
 minEdge = edgesAvailable.poll();
 }
 listMinEdges.add(minEdge); // this list will contain unique targetvertices.
 sourceVertex = minEdge.getTarget(); // get the next vertex.
 unvisitedVertices.remove(sourceVertex);
 }
 return listMinEdges;
 } 
 public static void main(String[] args) {
 GraphPrims<Character> graphPrims = new GraphPrims<Character>();
 graphPrims.addEgde('A', 'B', 10);
 graphPrims.addEgde('A', 'C', 15);
 graphPrims.addEgde('C', 'B', 50);
 graphPrims.addEgde('C', 'D', 20);
 graphPrims.addEgde('B', 'D', 80);
 graphPrims.addEgde('B', 'F', 80); 
 // graphPrims.addEgde('x', 'y', 700); 
 for (EdgePrims<Character> edge : getMinSpanTree(graphPrims)) {
 System.out.println(edge.toString());
 }
 }
}
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Dec 2, 2013 at 5:58
\$\endgroup\$

1 Answer 1

6
\$\begingroup\$
  1. public EdgePrims(T node1, T node2, int distance) - What are node1 and node2? From reading the source one can see they are source and target node - so they should be named accordingly. The names of the parameters of a function are an important piece of documentation and should convey their meaning.

  2. You have created a link between your data structures (GraphPrims, EdgePrims) and an algorithm which seems weird as they have only in common that Prim's is a graph algorithm - meaning you need a graph to run it on but the graph doesn't need the algorithm. I'm pretty sure I could implement Dijkstra's algorithm and run it on a graph of yours.

    The problem is that this creates a barrier in your mind for the re-usability of the classes. Also it reads odd if I write

    class Dijkstras 
    {
     public static <T> List<EdgePrims<T>> getShortestPath(GraphPrims<T> graph, EdgePrims<T> from, EdgePrims<T> to)
     { ... }
    }
    
  3. Why do you initialize edgesAvailable with a default initial capacity of 10? According to the Java documentation the default initial capacity is 11. Why is 10 any better?

answered Dec 2, 2013 at 9:01
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.