Gráficos em Java

Gráficos em Java

1. Visão geral

Neste tutorial,we’ll understand the basic concepts of a graph as a data structure.

Também exploraremos sua implementação em Java, juntamente com várias operações possíveis em um gráfico. Também discutiremos as bibliotecas Java que oferecem implementações gráficas.

2. Estrutura de dados do gráfico

Um gráfico é umdata structure for storing connected data como uma rede de pessoas em uma plataforma de mídia social.

Um gráfico consiste em vértices e arestas. A vertex represents the entity (por exemplo, pessoas) ean edge represents the relationship between entities (por exemplo, as amizades de uma pessoa).

Vamos definir um gráfico simples para entender isso melhor:

image image

Aqui, definimos um gráfico simples com cinco vértices e seis arestas. Os círculos são vértices que representam pessoas e as linhas que conectam dois vértices são arestas que representam amigos em um portal online.

Existem algumas variações desse gráfico simples, dependendo das propriedades das arestas. Vamos examiná-los brevemente nas próximas seções.

No entanto, vamos nos concentrar apenas no gráfico simples apresentado aqui para os exemplos Java neste tutorial.

2.1. Gráfico Dirigido

O gráfico que definimos até agora tem bordas sem qualquer direção. Se essesedges feature a direction in them, o gráfico resultante é conhecido como gráfico direcionado.

Um exemplo disso pode ser a representação de quem envia a solicitação de amizade em uma amizade no portal online:

image image

Aqui, podemos ver que as arestas têm uma direção fixa. As arestas também podem ser bidirecionais.

2.2. Gráfico Ponderado

Novamente, nosso gráfico simples possui arestas que são imparciais ou não ponderadas. Se, em vez disso, essesedges carry relative weight, este gráfico é conhecido como gráfico ponderado.

Um exemplo de uma aplicação prática disso pode representar a idade relativamente antiga de uma amizade no portal online:

image image

Aqui, podemos ver que as arestas têm pesos associados a elas. Isso fornece um significado relativo a essas arestas.

3. Graph Representations

Um gráfico pode ser representado em diferentes formas, como matriz de adjacência e lista de adjacências. Cada um tem seus prós e contras em uma configuração diferente.

Apresentaremos essas representações de gráfico nesta seção.

3.1. Matriz de adjacência

Uma matriz de adjacência éa square matrix with dimensions equivalent to the number of vertices no gráfico.

Os elementos da matriz normalmente têm valores '0' ou '1'. Um valor de ‘1 'indica adjacência entre os vértices na linha e coluna e um valor de‘ 0', caso contrário.

Vamos ver como a matriz de adjacência se parece com nosso gráfico simples da seção anterior:

image image

Esta representação é bastanteeasier to implement and efficient to query também. No entanto, éless efficient with respect to space occupied.

3.2. Lista de adjacências

Uma lista de adjacências nada mais é do quean array of lists. O tamanho da matriz é equivalente ao número de vértices no gráfico.

A lista em um índice específico da matriz representa os vértices adjacentes do vértice representado por esse índice da matriz.

Vejamos como é a lista de adjacências para nosso gráfico simples da seção anterior:

image image

Essa representação écomparatively difficult to create and less efficient to query. No entanto, oferecebetter space efficiency.

Usaremos a lista de adjacências para representar o gráfico neste tutorial.

4. Gráficos em Java

Java não tem uma implementação padrão da estrutura de dados do gráfico.

No entanto, podemos implementar o gráfico usando Java Collections.

Vamos começar definindo um vértice:

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

    // equals and hashCode
}

A definição de vértice acima apresenta apenas um rótulo, mas pode representar qualquer entidade possível comoPerson ouCity.

Além disso, observe quewe have to override the equals() and hashCode() methods as these are necessary to work with Java Collections.

Como discutimos anteriormente, um gráfico nada mais é do que uma coleção de vértices e arestas que podem ser representadas como uma matriz de adjacência ou uma lista de adjacência.

Vamos ver como podemos definir isso usando uma lista de adjacências aqui:

class Graph {
    private Map> adjVertices;

    // standard constructor, getters, setters
}

Como podemos ver aqui, a classeGraph está usandoMap das coleções Java para definir a lista de adjacências.

Existem várias operações possíveis em uma estrutura de dados de gráfico, comocreating, updating or searching through the graph.

Vamos passar por algumas das operações mais comuns e ver como podemos implementá-las em Java.

5. Operações de mutação de gráfico

Para começar, vamos definir alguns métodos para alterar a estrutura de dados do gráfico.

Vamos definir métodos para adicionar e remover vértices:

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));
}

Esses métodos simplesmente adicionam e removem elementos devertices Set.

Agora, vamos também definir um método para adicionar uma borda:

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);
}

Este método cria um novoEdge e atualiza os vértices adjacentesMap.

De forma semelhante, definiremos o métodoremoveEdge():

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);
}

A seguir, vamos ver como podemos criar o gráfico simples que desenhamos anteriormente usando os métodos que definimos até agora:

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;
}

Finalmente definiremos um método para obter os vértices adjacentes de um determinado vértice:

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

6. Percorrendo um gráfico

Agora que temos estrutura e funções de dados do gráfico para criar e atualizá-lo, podemos definir algumas funções adicionais para percorrer o gráfico. Precisamos percorrer um gráfico para executar qualquer ação significativa, como pesquisa dentro do gráfico.

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

6.1. Profundidade - primeira travessia

Uma travessia em profundidade começa em um vértice de raiz arbitrário eexplores vertices as deeper as possible along each branch before exploring vertices at the same level.

Vamos definir um método para realizar a travessia em profundidade:

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;
}

Aqui,we’re using a Stack to store the vertices that need to be traversed.

Vamos executar isso no gráfico que criamos na subseção anterior:

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

Observe que estamos usando o vértice "Bob" como nossa raiz para o percurso aqui, mas pode ser qualquer outro vértice.

6.2. Traversal em largura

Comparativamente, uma travessia em largura começa em um vértice de raiz arbitrário eexplores all neighboring vertices at the same level before going deeper no gráfico.

Agora, vamos definir um método para realizar a travessia em largura:

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;
}

Observe que uma travessia em larguramakes use of Queue to store the vertices which need to be traversed.

Vamos executar novamente esta travessia no mesmo gráfico:

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

Novamente, o vértice raiz que é "Bob" aqui também pode ser qualquer outro vértice.

7. Bibliotecas Java para gráficos

Não é necessário implementar sempre o gráfico do zero em Java. Existem várias bibliotecas abertas e maduras disponíveis, que oferecem implementações gráficas.

Nas próximas subseções, veremos algumas dessas bibliotecas.

7.1. JGraphT

JGraphT é uma das bibliotecas mais populares em Java para a estrutura de dados de gráfico. Permite a criação de um gráfico simples, gráfico direcionado, gráfico ponderado, entre outros.

Além disso, oferece muitos algoritmos possíveis na estrutura de dados do gráfico. Um de nossos tutoriais anteriores cobreJGraphT in much more detail.

7.2. Google Guava

Google Guava é um conjunto de bibliotecas Java que oferece uma variedade de funções, incluindo estrutura de dados de gráfico e seus algoritmos.

Ele suporta a criação deGraph simples,ValueGraph eNetwork. Eles podem ser definidos comoMutable ouImmutable.

7.3. Apache Commons

Apache Commons é um projeto Apache que oferece componentes Java reutilizáveis. Isso inclui o Commons Graph, que oferece um kit de ferramentas para criar e gerenciar a estrutura de dados do gráfico. Isso também fornece algoritmos gráficos comuns para operar na estrutura de dados.

7.4. Sourceforge JUNG

Java Universal Network/Graph (JUNG) é uma estrutura Java que fornece linguagem extensível para modelagem, análise e visualização de quaisquer dados que podem ser representados como um gráfico.

O JUNG suporta vários algoritmos que incluem rotinas como clustering, decomposição e otimização.

 

Essas bibliotecas fornecem várias implementações baseadas na estrutura de dados do gráfico. Existem tambémmore powerful frameworks based on graphs, comoApache Giraph, atualmente usado no Facebook para analisar o gráfico formado por seus usuários, eApache TinkerPop, comumente usado no topo de bancos de dados de gráficos.

8. Conclusão

Neste artigo, discutimos o gráfico como uma estrutura de dados, juntamente com suas representações. Definimos um gráfico muito simples em Java usando Java Collections e também definimos passagens comuns para o gráfico.

Também conversamos brevemente sobre várias bibliotecas disponíveis em Java fora da plataforma Java, que fornece implementações gráficas.

Como sempre, o código dos exemplos está disponívelover on GitHub.