Grafiken in Java

Grafiken in Java

1. Überblick

In diesem Tutorial werdenwe’ll understand the basic concepts of a graph as a data structure.

Wir werden auch die Implementierung in Java sowie verschiedene Operationen untersuchen, die in einem Diagramm möglich sind. Wir werden auch die Java-Bibliotheken diskutieren, die Diagrammimplementierungen anbieten.

2. Diagrammdatenstruktur

Ein Diagramm ist eindata structure for storing connected data wie ein Netzwerk von Personen auf einer Social-Media-Plattform.

Ein Diagramm besteht aus Eckpunkten und Kanten. A vertex represents the entity (zum Beispiel Personen) undan edge represents the relationship between entities (zum Beispiel die Freundschaften einer Person).

Definieren wir ein einfaches Diagramm, um dies besser zu verstehen:

image image

Hier haben wir ein einfaches Diagramm mit fünf Eckpunkten und sechs Kanten definiert. Die Kreise sind Eckpunkte, die Personen darstellen, und die Linien, die zwei Eckpunkte verbinden, sind Kanten, die Freunde in einem Online-Portal darstellen.

Abhängig von den Eigenschaften der Kanten gibt es einige Variationen dieses einfachen Diagramms. Lassen Sie uns sie in den nächsten Abschnitten kurz durchgehen.

Wir konzentrieren uns jedoch nur auf das einfache Diagramm, das hier für die Java-Beispiele in diesem Lernprogramm dargestellt wird.

2.1. Gerichteter Graph

Das Diagramm, das wir bisher definiert haben, hat Kanten ohne Richtung. Wenn dieseedges feature a direction in them sind, wird der resultierende Graph als gerichteter Graph bezeichnet.

Ein Beispiel hierfür kann darstellen, wer die Freundschaftsanfrage in einer Freundschaft auf dem Online-Portal versendet:

image image

Hier sehen wir, dass die Kanten eine feste Richtung haben. Die Kanten können auch bidirektional sein.

2.2. Gewichteter Graph

Auch hier hat unser einfaches Diagramm Kanten, die unbefangen oder ungewichtet sind. Wenn stattdessen dieseedges carry relative weight, wird dieses Diagramm als gewichtetes Diagramm bezeichnet.

Ein Beispiel für eine praktische Anwendung kann sein, wie alt eine Freundschaft im Online-Portal ist:

image image

Hier können wir sehen, dass die Kanten mit Gewichten verknüpft sind. Dies gibt diesen Kanten eine relative Bedeutung.

3. Graph Representations

Ein Diagramm kann in verschiedenen Formen dargestellt werden, beispielsweise als Adjazenzmatrix und Adjazenzliste. Jeder hat seine Vor- und Nachteile in einer anderen Konfiguration.

Wir werden diese Diagrammdarstellungen in diesem Abschnitt vorstellen.

3.1. Adjazenzmatrix

Eine Adjazenzmatrix ista square matrix with dimensions equivalent to the number of vertices im Diagramm.

Die Elemente der Matrix haben typischerweise die Werte '0' oder '1'. Ein Wert von "1" gibt die Nachbarschaft zwischen den Eckpunkten in der Zeile und der Spalte und ansonsten einen Wert von "0" an.

Lassen Sie uns sehen, wie die Adjazenzmatrix für unser einfaches Diagramm aus dem vorherigen Abschnitt aussieht:

image image

Diese Darstellung beträgt ebenfalls ziemlicheasier to implement and efficient to query. Es ist jedochless efficient with respect to space occupied.

3.2. Adjazenzliste

Eine Adjazenzliste ist nichts anderes alsan array of lists. Die Größe des Arrays entspricht der Anzahl der Scheitelpunkte im Diagramm.

Die Liste an einem bestimmten Index des Arrays repräsentiert die benachbarten Scheitelpunkte des Scheitelpunkts, der durch diesen Array-Index dargestellt wird.

Lassen Sie uns sehen, wie die Adjazenzliste für unser einfaches Diagramm aus dem vorherigen Abschnitt aussieht:

image image

Diese Darstellung istcomparatively difficult to create and less efficient to query. Es bietet jedochbetter space efficiency.

Wir verwenden die Adjazenzliste, um das Diagramm in diesem Lernprogramm darzustellen.

4. Grafiken in Java

Java hat keine Standardimplementierung der Diagrammdatenstruktur.

Wir können das Diagramm jedoch mithilfe von Java-Sammlungen implementieren.

Beginnen wir mit der Definition eines Scheitelpunkts:

class Vertex {
    String label;
    Vertex(String label) {
        this.label = label;
    }

    // equals and hashCode
}

Die obige Definition des Scheitelpunkts enthält nur eine Beschriftung, dies kann jedoch jede mögliche Entität wiePerson oderCity. darstellen

Beachten Sie auch, dasswe have to override the equals() and hashCode() methods as these are necessary to work with Java Collections.

Wie bereits erwähnt, ist ein Graph nichts anderes als eine Sammlung von Eckpunkten und Kanten, die entweder als Adjazenzmatrix oder als Adjazenzliste dargestellt werden können.

Mal sehen, wie wir dies anhand einer Adjazenzliste hier definieren können:

class Graph {
    private Map> adjVertices;

    // standard constructor, getters, setters
}

Wie wir hier sehen können, verwendet die KlasseGraphMap aus Java-Sammlungen, um die Adjazenzliste zu definieren.

Für eine Diagrammdatenstruktur sind mehrere Operationen möglich, z. B.creating, updating or searching through the graph.

Wir werden einige der gängigsten Vorgänge durchgehen und sehen, wie wir sie in Java implementieren können.

5. Diagrammmutationsoperationen

Zunächst definieren wir einige Methoden zum Mutieren der Diagrammdatenstruktur.

Definieren wir Methoden zum Hinzufügen und Entfernen von Scheitelpunkten:

void addVertex(String label) {
    adjVertices.putIfAbsent(new Vertex(label), new ArrayList<>());
}

void removeVertex(String label) {
    Vertex v = new Vertex(label);
    adjVertices.values().stream().forEach(e -> e.remove(v));
    adjVertices.remove(new Vertex(label));
}

Diese Methoden fügen einfach Elemente zuvertices Sethinzu und entfernen sie.

Definieren wir nun auch eine Methode zum Hinzufügen einer Kante:

void addEdge(String label1, String label2) {
    Vertex v1 = new Vertex(label1);
    Vertex v2 = new Vertex(label2);
    adjVertices.get(v1).add(v2);
    adjVertices.get(v2).add(v1);
}

Diese Methode erstellt ein neuesEdge und aktualisiert die benachbarten EckpunkteMap.

In ähnlicher Weise definieren wir dieremoveEdge()-Methode:

void removeEdge(String label1, String label2) {
    Vertex v1 = new Vertex(label1);
    Vertex v2 = new Vertex(label2);
    List eV1 = adjVertices.get(v1);
    List eV2 = adjVertices.get(v2);
    if (eV1 != null)
        eV1.remove(v2);
    if (eV2 != null)
        eV2.remove(v1);
}

Als nächstes wollen wir sehen, wie wir das einfache Diagramm, das wir zuvor gezeichnet haben, mit den bisher definierten Methoden erstellen können:

Graph createGraph() {
    Graph graph = new Graph();
    graph.addVertex("Bob");
    graph.addVertex("Alice");
    graph.addVertex("Mark");
    graph.addVertex("Rob");
    graph.addVertex("Maria");
    graph.addEdge("Bob", "Alice");
    graph.addEdge("Bob", "Rob");
    graph.addEdge("Alice", "Mark");
    graph.addEdge("Rob", "Mark");
    graph.addEdge("Alice", "Maria");
    graph.addEdge("Rob", "Maria");
    return graph;
}

Wir werden schließlich eine Methode definieren, um die benachbarten Scheitelpunkte eines bestimmten Scheitelpunkts zu erhalten:

List getAdjVertices(String label) {
    return adjVertices.get(new Vertex(label));
}

6. Durchlaufen eines Graphen

Nachdem wir nun die Diagrammdatenstruktur und -funktionen zum Erstellen und Aktualisieren definiert haben, können wir einige zusätzliche Funktionen zum Durchlaufen des Diagramms definieren. Wir müssen einen Graphen durchlaufen, um eine sinnvolle Aktion wie die Suche im Graphen auszuführen.

Es gibttwo possible ways to traverse a graph, depth-first traversal and breadth-first traversal.

6.1. Tiefe-Erste Durchquerung

Eine Tiefendurchquerung beginnt an einem beliebigen Wurzelscheitelpunkt undexplores vertices as deeper as possible along each branch before exploring vertices at the same level.

Definieren wir eine Methode zum Durchführen der Tiefenüberquerung:

Set depthFirstTraversal(Graph graph, String root) {
    Set visited = new LinkedHashSet();
    Stack stack = new Stack();
    stack.push(root);
    while (!stack.isEmpty()) {
        String vertex = stack.pop();
        if (!visited.contains(vertex)) {
            visited.add(vertex);
            for (Vertex v : graph.getAdjVertices(vertex)) {
                stack.push(v.label);
            }
        }
    }
    return visited;
}

Hierwe’re using a Stack to store the vertices that need to be traversed.

Führen Sie dies für das Diagramm aus, das wir im vorherigen Unterabschnitt erstellt haben:

assertEquals("[Bob, Rob, Maria, Alice, Mark]", depthFirstTraversal(graph, "Bob").toString());

Bitte beachten Sie, dass wir hier den Scheitelpunkt "Bob" als Wurzel für die Durchquerung verwenden. Dies kann jedoch jeder andere Scheitelpunkt sein.

6.2. Breite zuerst durchqueren

Im Vergleich dazu beginnt eine Breitendurchquerung an einem beliebigen Wurzelscheitelpunkt undexplores all neighboring vertices at the same level before going deeper im Diagramm.

Definieren wir nun eine Methode, um die erste Durchquerung durchzuführen:

Set breadthFirstTraversal(Graph graph, String root) {
    Set visited = new LinkedHashSet();
    Queue queue = new LinkedList();
    queue.add(root);
    visited.add(root);
    while (!queue.isEmpty()) {
        String vertex = queue.poll();
        for (Vertex v : graph.getAdjVertices(vertex)) {
            if (!visited.contains(v.label)) {
                visited.add(v.label);
                queue.add(v.label);
            }
        }
    }
    return visited;
}

Beachten Sie, dass eine Breite zuerstmakes use of Queue to store the vertices which need to be traversed durchquert.

Führen Sie diese Durchquerung erneut in derselben Grafik aus:

assertEquals("[Bob, Alice, Rob, Mark, Maria]", breadthFirstTraversal(graph, "Bob").toString());

Auch hier kann der Wurzelscheitelpunkt, der hier „Bob“ ist, ein beliebiger anderer Scheitelpunkt sein.

7. Java-Bibliotheken für Grafiken

Es ist nicht erforderlich, das Diagramm in Java immer von Grund auf neu zu implementieren. Es gibt mehrere Open Source- und ausgereifte Bibliotheken, die Diagrammimplementierungen anbieten.

In den nächsten Unterabschnitten werden wir einige dieser Bibliotheken durchgehen.

7.1. JGraphT

JGraphT ist eine der beliebtesten Bibliotheken in Java für die Grafikdatenstruktur. Es ermöglicht unter anderem die Erstellung eines einfachen Diagramms, eines gerichteten Diagramms und eines gewichteten Diagramms.

Darüber hinaus bietet es viele mögliche Algorithmen für die Diagrammdatenstruktur. Eines unserer vorherigen Tutorials behandeltJGraphT in much more detail.

7.2. Google Guava

Google Guava ist eine Reihe von Java-Bibliotheken, die eine Reihe von Funktionen bieten, einschließlich der Grafikdatenstruktur und ihrer Algorithmen.

Es unterstützt das Erstellen einfacherGraph,ValueGraph undNetwork. Diese können alsMutable oderImmutable definiert werden.

7.3. Apache Commons

Apache Commons ist ein Apache-Projekt, das wiederverwendbare Java-Komponenten bietet. Dazu gehört Commons Graph, das ein Toolkit zum Erstellen und Verwalten der Diagrammdatenstruktur bietet. Dies stellt auch allgemeine Graph-Algorithmen zur Verfügung, um mit der Datenstruktur zu arbeiten.

7.4. Sourceforge JUNG

Java Universal Network/Graph (JUNG) ist ein Java-Framework, das eine erweiterbare Sprache für die Modellierung, Analyse und Visualisierung aller Daten bietet, die als Grafik dargestellt werden können.

JUNG unterstützt eine Reihe von Algorithmen, darunter Routinen wie Clustering, Dekomposition und Optimierung.

 

Diese Bibliotheken bieten eine Reihe von Implementierungen basierend auf der Diagrammdatenstruktur. Es gibt auchmore powerful frameworks based on graphs, wieApache Giraph, die derzeit bei Facebook verwendet werden, um das von ihren Benutzern erstellte Diagramm zu analysieren, undApache TinkerPop, die üblicherweise über Diagrammdatenbanken verwendet werden.

8. Fazit

In diesem Artikel haben wir das Diagramm als Datenstruktur mit seinen Darstellungen behandelt. Wir haben ein sehr einfaches Diagramm in Java mithilfe von Java-Sammlungen definiert und außerdem gemeinsame Durchquerungen für das Diagramm definiert.

Wir haben auch kurz über verschiedene in Java verfügbare Bibliotheken außerhalb der Java-Plattform gesprochen, die Diagrammimplementierungen bereitstellen.

Wie immer ist der Code für die Beispieleover on GitHub verfügbar.