All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrapht.alg.util.NeighborCache Maven / Gradle / Ivy

The newest version!
/*
 * (C) Copyright 2017-2023, by Szabolcs Besenyei and Contributors.
 *
 * JGraphT : a free Java graph-theory library
 *
 * See the CONTRIBUTORS.md file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the
 * GNU Lesser General Public License v2.1 or later
 * which is available at
 * http://www.gnu.org/licenses/old-licenses/lgpl-2.1-standalone.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR LGPL-2.1-or-later
 */
package org.jgrapht.alg.util;

import org.jgrapht.*;
import org.jgrapht.event.*;
import org.jgrapht.util.*;

import java.util.*;
import java.util.function.*;

/**
 * Maintains a cache of each vertex's neighbors. While lists of neighbors can be obtained from
 * {@link Graphs}, they are re-calculated at each invocation by walking a vertex's incident edges,
 * which becomes inordinately expensive when performed often.
 * 
 * 

* The cache also keeps track of successors and predecessors for each vertex. This means that the * result of the union of calling predecessorsOf(v) and successorsOf(v) is equal to the result of * calling neighborsOf(v) for a given vertex v. * * @param the vertex type * @param the edge type * * @author Szabolcs Besenyei */ public class NeighborCache implements GraphListener { private Map> successors = new HashMap<>(); private Map> predecessors = new HashMap<>(); private Map> neighbors = new HashMap<>(); private Graph graph; /** * Constructor * * @param graph the input graph * @throws NullPointerException if the input graph is null */ public NeighborCache(Graph graph) { this.graph = Objects.requireNonNull(graph); } /** * Returns the unique predecessors of the given vertex if it exists in the cache, otherwise it * is initialized. * * @param v the given vertex * @return the unique predecessors of the given vertex */ public Set predecessorsOf(V v) { return fetch(v, predecessors, k -> new Neighbors<>(Graphs.predecessorListOf(graph, v))); } /** * Returns the unique successors of the given vertex if it exists in the cache, otherwise it is * initialized. * * @param v the given vertex * @return the unique successors of the given vertex */ public Set successorsOf(V v) { return fetch(v, successors, k -> new Neighbors<>(Graphs.successorListOf(graph, v))); } /** * Returns the unique neighbors of the given vertex if it exists in the cache, otherwise it is * initialized. * * @param v the given vertex * @return the unique neighbors of the given vertex */ public Set neighborsOf(V v) { return fetch(v, neighbors, k -> new Neighbors<>(Graphs.neighborListOf(graph, v))); } /** * Returns a list of vertices which are adjacent to a specified vertex. If the graph is a * multigraph, vertices may appear more than once in the returned list. Because a list of * neighbors can not be efficiently maintained, it is reconstructed on every invocation, by * duplicating entries in the neighbor set. It is thus more efficient to use * {@link #neighborsOf} unless duplicate neighbors are important. * * @param v the vertex whose neighbors are desired * * @return all neighbors of the specified vertex */ public List neighborListOf(V v) { Neighbors nbrs = neighbors.get(v); if (nbrs == null) { nbrs = new Neighbors<>(Graphs.neighborListOf(graph, v)); neighbors.put(v, nbrs); } return nbrs.getNeighborList(); } private Set fetch(V vertex, Map> map, Function> func) { return map.computeIfAbsent(vertex, func).getNeighbors(); } @Override public void edgeAdded(GraphEdgeChangeEvent e) { assert e .getSource() == this.graph : "This NeighborCache is added as a listener to a graph other than the one specified during the construction of this NeighborCache!"; V source = e.getEdgeSource(); V target = e.getEdgeTarget(); if (successors.containsKey(source)) { successors.get(source).addNeighbor(target); } if (predecessors.containsKey(target)) { predecessors.get(target).addNeighbor(source); } if (neighbors.containsKey(source)) { neighbors.get(source).addNeighbor(target); } if (neighbors.containsKey(target)) { neighbors.get(target).addNeighbor(source); } } @Override public void edgeRemoved(GraphEdgeChangeEvent e) { assert e .getSource() == this.graph : "This NeighborCache is added as a listener to a graph other than the one specified during the construction of this NeighborCache!"; V source = e.getEdgeSource(); V target = e.getEdgeTarget(); if (successors.containsKey(source)) { successors.get(source).removeNeighbor(target); } if (predecessors.containsKey(target)) { predecessors.get(target).removeNeighbor(source); } if (neighbors.containsKey(source)) { neighbors.get(source).removeNeighbor(target); } if (neighbors.containsKey(target)) { neighbors.get(target).removeNeighbor(source); } } @Override public void vertexAdded(GraphVertexChangeEvent e) { // Nothing to cache until there are edges } @Override public void vertexRemoved(GraphVertexChangeEvent e) { assert e .getSource() == this.graph : "This NeighborCache is added as a listener to a graph other than the one specified during the construction of this NeighborCache!"; successors.remove(e.getVertex()); predecessors.remove(e.getVertex()); neighbors.remove(e.getVertex()); } /** * Stores cached neighbors for a single vertex. Includes support for live neighbor sets and * duplicate neighbors. */ static class Neighbors { private Map neighborCounts = new LinkedHashMap<>(); // TODO could eventually make neighborSet modifiable, resulting // in edge removals from the graph private Set neighborSet = Collections.unmodifiableSet(neighborCounts.keySet()); public Neighbors(Collection neighbors) { // add all current neighbors for (V neighbor : neighbors) { addNeighbor(neighbor); } } public void addNeighbor(V v) { ModifiableInteger count = neighborCounts.get(v); if (count == null) { count = new ModifiableInteger(1); neighborCounts.put(v, count); } else { count.increment(); } } public void removeNeighbor(V v) { ModifiableInteger count = neighborCounts.get(v); if (count == null) { throw new IllegalArgumentException( "Attempting to remove a neighbor that wasn't present"); } count.decrement(); if (count.getValue() == 0) { neighborCounts.remove(v); } } public Set getNeighbors() { return neighborSet; } public List getNeighborList() { List neighbors = new ArrayList<>(); for (Map.Entry entry : neighborCounts.entrySet()) { V v = entry.getKey(); int count = entry.getValue().intValue(); for (int i = 0; i < count; i++) { neighbors.add(v); } } return neighbors; } @Override public String toString() { return neighborSet.toString(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy