Refactoring

* rewritten marks (more performance for multiple vertices)
* fixed a bunch of instructions
* added a starting point for save/load graph instructions
This commit is contained in:
2019-04-21 17:52:59 +02:00
parent bf0f3abe04
commit 34302b6e92
7 changed files with 219 additions and 75 deletions

1
.gitignore vendored
View File

@@ -129,3 +129,4 @@ local.properties
.project
*.iml
.classpath
/.gradle/

15
build.gradle Normal file
View File

@@ -0,0 +1,15 @@
apply plugin: 'java'
apply plugin: 'eclipse'
version='1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
testCompile group: 'junit', name: 'junit', version: '4.+'
}

View File

@@ -1,13 +1,19 @@
package berack96.sim.util.graph;
import berack96.sim.util.graph.visit.VisitInfo;
import berack96.sim.util.graph.visit.VisitStrategy;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import com.google.gson.Gson;
import berack96.sim.util.graph.visit.VisitInfo;
import berack96.sim.util.graph.visit.VisitStrategy;
/**
* An interface for the graphs.<br>
* This interface is used for the graphs with Directed edges.<br>
@@ -508,15 +514,15 @@ public interface Graph<V, W extends Number> extends Iterable<V> {
Graph<V, W> subGraph(V source, int depth) throws NullPointerException, IllegalArgumentException;
/**
* Get a sub-graph of the current one with only the vertex marked with the selected marker.<br>
* Get a sub-graph of the current one with only the vertex marked with the selected markers.<br>
* Each vertex will have all his edges, but only the ones with the destination marked with the same marker.<br>
* If the marker is null then the returning graph will have all the vertices that are not marked by any marker.<br>
* If the graph doesn't contain any vertex with that marker then an empty graph is returned.
*
* @param marker the marker
* @param marker one or more markers
* @return a sub-graph of the current graph
*/
Graph<V, W> subGraph(Object marker);
Graph<V, W> subGraph(Object...marker);
/**
* Get the minimum path from the source vertex to the destination vertex.<br>
@@ -541,6 +547,35 @@ public interface Graph<V, W extends Number> extends Iterable<V> {
*/
Map<V, List<Edge<V, W>>> distance(V source) throws NullPointerException, IllegalArgumentException;
/*
static void save(Graph<?, ?> graph, String file) throws IOException {
Map<String, Object> map = new HashMap<>();
map.put("vertices", graph.vertices());
map.put("edges", graph.edges());
Gson gson = new Gson();
FileWriter writer = new FileWriter(file);
writer.write(gson.toJson(map));
writer.close();
}
@SuppressWarnings("unchecked")
static void load(Graph<Object, Number> graph, String file) throws IOException {
Gson gson = new Gson();
FileReader reader = new FileReader(file);
StringBuilder fileContent = new StringBuilder();
int c;
while((c = reader.read()) != -1)
fileContent.append(c);
reader.close();
Map<String, Object> map = gson.fromJson(fileContent.toString(), Map.class);
graph.addAllVertices((Collection<Object>)map.get("vertices"));
graph.addAllEdges((Collection<Edge<Object, Number>>)map.get("edges"));
}
*/
// TODO maybe -> STATIC saveOnFile(orString) INSTANCE loadFromFile(orString), but need JSON parser
// TODO maybe, but i don't think so... STATIC DISTANCE V* -> V*
}

View File

@@ -30,9 +30,11 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
private final Map<V, Map<V, W>> edges = new HashMap<>();
/**
* Map that contains the vertex as key and all the marker as the value associated with that vertex.
* Map that contains the marker as key and a set of all the vertices that has it as the value.<br>
* This map is build like this for performance in creating the marker for multiple vertices.<br>
* If you flip the parameters (object and set) then has more performance over the single vertex.
*/
private final Map<V, Set<Object>> marked = new HashMap<>();
private final Map<Object, Set<V>> markers = new HashMap<>();
/**
* Need this variable for not calculating each time the SCC or the cyclic part if the graph doesn't change
@@ -42,7 +44,7 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
/**
* Need this variable for not calculating each time the distance from a vertex to all his destinations if the graph doesn't change
*/
private Map<V, Dijkstra<V, W>> dijkstra = null;
private Map<V, Dijkstra<V, W>> dijkstra = new HashMap<>();
@Override
public boolean isCyclic() {
@@ -63,8 +65,8 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
@Override
public void addVertex(V vertex) throws NullPointerException {
checkNull(vertex);
graphChanged();
edges.put(vertex, new HashMap<>());
graphChanged();
}
@Override
@@ -84,16 +86,18 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
@Override
public void removeVertex(V vertex) throws NullPointerException {
if (contains(vertex)) {
graphChanged();
edges.remove(vertex);
edges.forEach((v, map) -> map.remove(vertex));
markers.forEach((mark, set) -> set.remove(vertex));
graphChanged();
}
}
@Override
public void removeAllVertex() {
graphChanged();
edges.clear();
markers.clear();
graphChanged();
}
@Override
@@ -107,50 +111,51 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
checkNullAndExist(vertex);
checkNull(mark);
Set<Object> set = marked.computeIfAbsent(vertex, (m) -> new HashSet<>());
set.add(mark);
Set<V> set = markers.computeIfAbsent(mark, (v) -> new HashSet<>());
set.add(vertex);
}
@Override
public void unMark(V vertex, Object mark) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
checkNull(mark);
marked.get(vertex).remove(mark);
markers.get(mark).remove(vertex);
}
@Override
public void unMark(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
marked.get(vertex).clear();
markers.forEach( (mark, set) -> set.remove(vertex) );
}
@Override
public Collection<V> getMarkedWith(Object mark) throws NullPointerException {
checkNull(mark);
Collection<V> ret = new HashSet<V>();
marked.forEach((v, set) -> {
if(set.contains(mark))
ret.add(v);
});
return ret;
return markers.computeIfAbsent(mark, (v) -> new HashSet<>());
}
@Override
public Set<Object> getMarks(V vertex) throws NullPointerException, IllegalArgumentException {
public Collection<Object> getMarks(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
return marked.computeIfAbsent(vertex, (m) -> new HashSet<>());
Collection<Object> marks = new HashSet<>();
markers.forEach( (mark, set) -> {
if (set.contains(vertex))
marks.add(mark);
});
return marks;
}
@Override
public void unMarkAll(Object mark) {
checkNull(mark);
marked.forEach((v, m) -> m.remove(mark));
markers.remove(mark);
}
@Override
public void unMarkAll() {
marked.clear();
markers.clear();
}
@Override
@@ -159,8 +164,9 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
checkNullAndExist(vertex2);
checkNull(weight);
W old = edges.get(vertex1).put(vertex2, weight);
graphChanged();
return edges.get(vertex1).put(vertex2, weight);
return old;
}
@Override
@@ -198,24 +204,24 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
checkNullAndExist(vertex1);
checkNullAndExist(vertex2);
graphChanged();
edges.get(vertex1).remove(vertex2);
graphChanged();
}
@Override
public void removeAllInEdge(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
graphChanged();
edges.forEach((v, map) -> map.remove(vertex));
graphChanged();
}
@Override
public void removeAllOutEdge(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
graphChanged();
edges.put(vertex, new HashMap<>());
graphChanged();
}
@Override
@@ -227,8 +233,8 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
@Override
public void removeAllEdge() {
graphChanged();
edges.forEach((v, map) -> map.clear());
graphChanged();
}
@Override
@@ -237,19 +243,19 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
}
@Override
public Set<V> vertices() {
public Collection<V> vertices() {
return new HashSet<>(edges.keySet());
}
@Override
public Set<Edge<V, W>> edges() {
public Collection<Edge<V, W>> edges() {
Set<Edge<V, W>> allEdges = new HashSet<>();
edges.forEach((source, map) -> map.forEach((destination, weight) -> allEdges.add(new Edge<>(source, destination, weight))));
return allEdges;
}
@Override
public Set<Edge<V, W>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
public Collection<Edge<V, W>> edgesOf(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
Set<Edge<V, W>> set = new HashSet<>();
@@ -282,14 +288,14 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
}
@Override
public Set<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
public Collection<V> getChildren(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
return new HashSet<>(edges.get(vertex).keySet());
}
@Override
public Set<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
public Collection<V> getAncestors(V vertex) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(vertex);
Set<V> set = new HashSet<>();
@@ -406,32 +412,36 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
}
@Override
public Graph<V, W> subGraph(final Object marker) {
final Graph<V, W> graph = new MapGraph<>();
public Graph<V, W> subGraph(final Object...marker) {
final Graph<V, W> sub = new MapGraph<>();
final Set<V> allVertices = new HashSet<>();
final Set<Object> allMarkers = new HashSet<>();
if (marker != null)
for (Object mark: marker)
allMarkers.add(mark);
marked.forEach((vertex, mark) -> {
if (mark.contains(marker) || (marker == null && !mark.isEmpty()))
allVertices.add(vertex);
markers.forEach( (mark, set) -> {
if (marker == null || allMarkers.contains(mark))
allVertices.addAll(set);
});
if (marker == null) {
Collection<V> toAdd = graph.vertices();
Collection<V> toAdd = vertices();
toAdd.removeAll(allVertices);
allVertices.clear();
allVertices.addAll(toAdd);
}
graph.addAllVertices(allVertices);
for (V vertex : graph.vertices())
getEdgesOut(vertex).forEach((edge) -> {
sub.addAllVertices(allVertices);
for (V vertex : sub.vertices())
edges.get(vertex).forEach( (dest, weight) -> {
try {
graph.addEdge(edge);
} catch (Exception ignored) {
}
sub.addEdge(vertex, dest, weight);
} catch (Exception ignored) {}
});
return graph;
return sub;
}
@Override
@@ -439,7 +449,7 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
checkNullAndExist(source);
checkNullAndExist(destination);
Dijkstra<V, W> dijkstra = getDijkstra(source);
Dijkstra<V, W> dijkstra = getDijkstra(source); /* Cached */
List<Edge<V, W>> path = dijkstra.getLastDistance().get(destination);
if (path == null)
throw new UnsupportedOperationException(NOT_CONNECTED);
@@ -449,7 +459,7 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
@Override
public Map<V, List<Edge<V, W>>> distance(V source) throws NullPointerException, IllegalArgumentException {
checkNullAndExist(source);
return new HashMap<>(getDijkstra(source).getLastDistance());
return new HashMap<>(getDijkstra(source).getLastDistance()); /* Cached */
}
@Override
@@ -459,16 +469,20 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
/**
* Simple function that set all the memory vars at null if the graph changed
* Simple function that reset all the caching variables if the graph changed
*/
private void graphChanged() {
tarjan = null;
dijkstra = null;
dijkstra.clear();
}
/**
* Simple function that return the result of the Dijkstra visit, with the starting point as source.<br>
* It also cache it, so multiple call will return always the same value unless the graph has changed.
* @param source the source of the visit
* @return the complete visit
*/
private Dijkstra<V, W> getDijkstra(V source) {
if (dijkstra == null)
dijkstra = new HashMap<>();
if (dijkstra.get(source) == null) {
Dijkstra<V, W> newDijkstra = new Dijkstra<>();
newDijkstra.visit(this, source, null);
@@ -478,6 +492,11 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
return dijkstra.get(source);
}
/**
* Simple function that return the result of the Tarjan visit.<br>
* It also cache it, so multiple call will return always the same value unless the graph has changed.
* @return the tarjan visit
*/
private Tarjan<V, W> getTarjan() {
if (tarjan == null) {
tarjan = new Tarjan<>();
@@ -487,11 +506,21 @@ public class MapGraph<V, W extends Number> implements Graph<V, W> {
return tarjan;
}
/**
* Test if the object passed is null.
* If it is throw an exception.
* @param object the object to test
*/
private void checkNull(Object object) {
if (object == null)
throw new NullPointerException(PARAM_NULL);
}
/**
* Check if the vertex passed is null and if exist in the graph.
* If not then throws eventual exception
* @param vertex the vertex to test
*/
private void checkNullAndExist(V vertex) {
checkNull(vertex);
if (!edges.containsKey(vertex))

View File

@@ -1,17 +1,34 @@
package berack96.sim.util.graph.view;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ItemEvent;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.BevelBorder;
import com.google.gson.Gson;
import berack96.sim.util.graph.Graph;
import berack96.sim.util.graph.view.edge.EdgeListener;
import berack96.sim.util.graph.view.vertex.VertexListener;
import berack96.sim.util.graph.visit.VisitStrategy;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.util.*;
import java.util.List;
public class GraphInfo<V, W extends Number> extends JPanel {
private static final long serialVersionUID = 1L;
@@ -122,10 +139,40 @@ public class GraphInfo<V, W extends Number> extends JPanel {
panelVertex.setOpaque(false);
panelVertex.setLayout(new GridLayout(components.size()/2, 2, 2*2, 2*2));
components.forEach(panelVertex::add);
components.clear();
/* Save/Load
JPanel panelSave = new JPanel(); */
/* SAVE/LOAD */
/*
JTextField fileText = new JTextField();
JButton saveB = new JButton("Save");
saveB.addActionListener(a -> {
try {
Graph.save(graphPanel.getGraph(), fileText.getText());
} catch (IOException e1) {
e1.printStackTrace();
}
});
JButton loadB = new JButton("Load");
loadB.addActionListener(a -> {
try {
Graph<V, W> graph = graphPanel.getGraph();
Graph.load((Graph<Object, Number>) graph, fileText.getText());
} catch (IOException e1) {
e1.printStackTrace();
}
});
components.add(new JLabel("File to save/load: "));
components.add(fileText);
components.add(saveB);
components.add(loadB);
JPanel panelSave = new JPanel();
panelSave.setOpaque(false);
panelSave.setBorder(BorderFactory.createLineBorder(Color.RED));
panelSave.setLayout(new GridLayout(components.size()/2, 2, 2*2, 2*2));
components.forEach(panelSave::add);
components.clear();
*/
/* ADDING COMPONENTS */
this.setBackground(Color.LIGHT_GRAY);
@@ -135,7 +182,7 @@ public class GraphInfo<V, W extends Number> extends JPanel {
this.add(panelDescription);
this.add(panelInfo);
this.add(panelVertex);
this.add(Box.createVerticalGlue());
/*this.add(panelSave);*/
modVertex.doClick();

View File

@@ -50,7 +50,6 @@ public class GraphWindow<V, W extends Number> extends JFrame {
this.infoPanel = new GraphInfo<>(graphPanel, vListener, eListener, strats);
this.graphPanel = graphPanel;
this.add(infoPanel, BorderLayout.EAST);
this.add(graphPanel);
}

View File

@@ -1022,11 +1022,11 @@ public class TestGraph {
/*
* This graph should be like this
*
* 1 -> 2 <- 6
* ^
* | | |
* 1 -> 2 <- 6 7
* ^ ^
* | | | |
* v v
* 3 <- 5 -> 4
* 3 <- 5 -> 4 8
*/
graph.addVertexIfAbsent("1");
@@ -1035,6 +1035,8 @@ public class TestGraph {
graph.addVertexIfAbsent("4");
graph.addVertexIfAbsent("5");
graph.addVertexIfAbsent("6");
graph.addVertexIfAbsent("7");
graph.addVertexIfAbsent("8");
graph.addEdge("1", "2", 1);
graph.addEdge("1", "3", 1);
@@ -1043,6 +1045,7 @@ public class TestGraph {
graph.addEdge("5", "3", 2);
graph.addEdge("5", "4", 5);
graph.addEdge("6", "2", 2);
graph.addEdge("8", "7", 9);
graph.mark("1", "blue");
graph.mark("3", "blue");
@@ -1104,14 +1107,14 @@ public class TestGraph {
/* MARKED */
sub = graph.subGraph("z");
shouldContain(sub.vertices(), "1", "2", "5", "4");
shouldContain(sub.vertices(), "1", "2", "4", "5");
shouldContain(sub.edges(),
new Edge<>("1", "2", 1),
new Edge<>("2", "5", 4),
new Edge<>("5", "4", 5));
sub = graph.subGraph("circle");
shouldContain(sub.vertices(), "2", "5", "4", "6");
shouldContain(sub.vertices(), "2", "4", "5", "6");
shouldContain(sub.edges(),
new Edge<>("2", "5", 4),
new Edge<>("4", "6", 6),
@@ -1129,6 +1132,21 @@ public class TestGraph {
shouldContain(sub.edges(),
new Edge<>("4", "6", 6),
new Edge<>("6", "2", 2));
sub = graph.subGraph("blue", "circle");
shouldContain(sub.vertices(), "1", "2", "3", "4", "5", "6");
shouldContain(sub.edges(),
new Edge<>("1", "2", 1),
new Edge<>("1", "3", 1),
new Edge<>("2", "5", 4),
new Edge<>("4", "6", 6),
new Edge<>("5", "3", 2),
new Edge<>("5", "4", 5),
new Edge<>("6", "2", 2));
sub = graph.subGraph(null);
shouldContain(sub.vertices(), "7", "8");
shouldContain(sub.edges(), new Edge<>("8", "7", 9));
}
@Test