com.google.common.graph.StandardValueGraph Maven / Gradle / Ivy
/*
* Copyright (C) 2016 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.graph;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.graph.GraphConstants.DEFAULT_NODE_COUNT;
import static com.google.common.graph.Graphs.checkNonNegative;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.annotation.CheckForNull;
/**
* Standard implementation of {@link ValueGraph} that supports the options supplied by {@link
* AbstractGraphBuilder}.
*
* This class maintains a map of nodes to {@link GraphConnections}.
*
*
Collection-returning accessors return unmodifiable views: the view returned will reflect
* changes to the graph (if the graph is mutable) but may not be modified by the user.
*
*
The time complexity of all collection-returning accessors is O(1), since views are returned.
*
* @author James Sexton
* @author Joshua O'Madadhain
* @author Omar Darwish
* @param Node parameter type
* @param Value parameter type
*/
@ElementTypesAreNonnullByDefault
class StandardValueGraph extends AbstractValueGraph {
private final boolean isDirected;
private final boolean allowsSelfLoops;
private final ElementOrder nodeOrder;
final MapIteratorCache> nodeConnections;
long edgeCount; // must be updated when edges are added or removed
/** Constructs a graph with the properties specified in {@code builder}. */
StandardValueGraph(AbstractGraphBuilder super N> builder) {
this(
builder,
builder.nodeOrder.>createMap(
builder.expectedNodeCount.or(DEFAULT_NODE_COUNT)),
0L);
}
/**
* Constructs a graph with the properties specified in {@code builder}, initialized with the given
* node map.
*/
StandardValueGraph(
AbstractGraphBuilder super N> builder,
Map> nodeConnections,
long edgeCount) {
this.isDirected = builder.directed;
this.allowsSelfLoops = builder.allowsSelfLoops;
this.nodeOrder = builder.nodeOrder.cast();
// Prefer the heavier "MapRetrievalCache" for nodes if lookup is expensive.
this.nodeConnections =
(nodeConnections instanceof TreeMap)
? new MapRetrievalCache>(nodeConnections)
: new MapIteratorCache>(nodeConnections);
this.edgeCount = checkNonNegative(edgeCount);
}
@Override
public Set nodes() {
return nodeConnections.unmodifiableKeySet();
}
@Override
public boolean isDirected() {
return isDirected;
}
@Override
public boolean allowsSelfLoops() {
return allowsSelfLoops;
}
@Override
public ElementOrder nodeOrder() {
return nodeOrder;
}
@Override
public Set adjacentNodes(N node) {
return checkedConnections(node).adjacentNodes();
}
@Override
public Set predecessors(N node) {
return checkedConnections(node).predecessors();
}
@Override
public Set successors(N node) {
return checkedConnections(node).successors();
}
@Override
public Set> incidentEdges(N node) {
GraphConnections connections = checkedConnections(node);
return new IncidentEdgeSet(this, node) {
@Override
public Iterator> iterator() {
return connections.incidentEdgeIterator(node);
}
};
}
@Override
public boolean hasEdgeConnecting(N nodeU, N nodeV) {
return hasEdgeConnectingInternal(checkNotNull(nodeU), checkNotNull(nodeV));
}
@Override
public boolean hasEdgeConnecting(EndpointPair endpoints) {
checkNotNull(endpoints);
return isOrderingCompatible(endpoints)
&& hasEdgeConnectingInternal(endpoints.nodeU(), endpoints.nodeV());
}
@Override
@CheckForNull
public V edgeValueOrDefault(N nodeU, N nodeV, @CheckForNull V defaultValue) {
return edgeValueOrDefaultInternal(checkNotNull(nodeU), checkNotNull(nodeV), defaultValue);
}
@Override
@CheckForNull
public V edgeValueOrDefault(EndpointPair endpoints, @CheckForNull V defaultValue) {
validateEndpoints(endpoints);
return edgeValueOrDefaultInternal(endpoints.nodeU(), endpoints.nodeV(), defaultValue);
}
@Override
protected long edgeCount() {
return edgeCount;
}
private final GraphConnections checkedConnections(N node) {
GraphConnections connections = nodeConnections.get(node);
if (connections == null) {
checkNotNull(node);
throw new IllegalArgumentException("Node " + node + " is not an element of this graph.");
}
return connections;
}
final boolean containsNode(@CheckForNull N node) {
return nodeConnections.containsKey(node);
}
private final boolean hasEdgeConnectingInternal(N nodeU, N nodeV) {
GraphConnections connectionsU = nodeConnections.get(nodeU);
return (connectionsU != null) && connectionsU.successors().contains(nodeV);
}
@CheckForNull
private final V edgeValueOrDefaultInternal(N nodeU, N nodeV, @CheckForNull V defaultValue) {
GraphConnections connectionsU = nodeConnections.get(nodeU);
V value = (connectionsU == null) ? null : connectionsU.value(nodeV);
// TODO(b/192579700): Use a ternary once it no longer confuses our nullness checker.
if (value == null) {
return defaultValue;
} else {
return value;
}
}
}