diff --git a/guava-tests/test/com/google/common/graph/GraphEqualsTest.java b/guava-tests/test/com/google/common/graph/GraphEqualsTest.java index 314064d5e7bf..e686e5a07515 100644 --- a/guava-tests/test/com/google/common/graph/GraphEqualsTest.java +++ b/guava-tests/test/com/google/common/graph/GraphEqualsTest.java @@ -36,6 +36,7 @@ public final class GraphEqualsTest { private static final Integer N2 = 2; private static final Integer N3 = 3; + private static final String E11 = "1-1"; private static final String E12 = "1-2"; private static final String E12_A = "1-2a"; private static final String E13 = "1-3"; @@ -96,12 +97,11 @@ public void equals_edgeSetsDiffer() { new EqualsTester().addEqualityGroup(graph).addEqualityGroup(g2).testEquals(); } - // Node/edge sets are the same, but types differ. + // Node/edge sets are the same, but node/edge connections differ due to graph type. @Test - public void equals_typesDiffer() { + public void equals_directedVsUndirected() { graph.addEdge(E12, N1, N2); - // Whatever graphType specifies, pick another type. Graph g2; switch (graphType) { case UNDIRECTED: @@ -119,7 +119,30 @@ public void equals_typesDiffer() { new EqualsTester().addEqualityGroup(graph).addEqualityGroup(g2).testEquals(); } - // Node/edge sets and graph type are the same, but node/edge connections differ. + // Node/edge sets and node/edge connections are the same, but types differ. + // (In this case the graphs are considered equal; the type differences are irrelevant.) + @Test + public void equals_selfLoop_directedVsUndirected() { + graph.addEdge(E11, N1, N1); + + Graph g2; + switch (graphType) { + case UNDIRECTED: + g2 = Graphs.createDirected(); + break; + case DIRECTED: + g2 = Graphs.createUndirected(); + break; + default: + throw new IllegalStateException("Unexpected graph type: " + graphType); + } + + g2.addEdge(E11, N1, N1); + + new EqualsTester().addEqualityGroup(graph, g2).testEquals(); + } + + // Node/edge sets are the same, but node/edge connections differ. @Test public void equals_connectionsDiffer() { graph.addEdge(E12, N1, N2); @@ -133,7 +156,7 @@ public void equals_connectionsDiffer() { new EqualsTester().addEqualityGroup(graph).addEqualityGroup(g2).testEquals(); } - // Node/edge sets, graph type, and node/edge connections are the same, but GraphConfigs differ. + // Node/edge sets and node/edge connections are the same, but GraphConfigs differ. // (In this case the graphs are considered equal; the config differences are irrelevant.) @Test public void equals_configsDiffer() { @@ -145,7 +168,7 @@ public void equals_configsDiffer() { new EqualsTester().addEqualityGroup(graph, g2).testEquals(); } - // Node/edge sets, graph type, and node/edge connections are the same, but edge order differs. + // Node/edge sets and node/edge connections are the same, but edge order differs. // (In this case the graphs are considered equal; the edge add orderings are irrelevant.) @Test public void equals_edgeAddOrdersDiffer() { diff --git a/guava/src/com/google/common/graph/AbstractGraph.java b/guava/src/com/google/common/graph/AbstractGraph.java new file mode 100644 index 000000000000..134a02df1c8a --- /dev/null +++ b/guava/src/com/google/common/graph/AbstractGraph.java @@ -0,0 +1,79 @@ +/* + * 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 javax.annotation.Nullable; + +/** + * This class provides a skeletal implementation of {@link Graph}. It is recommended to extend this + * class rather than implement {@link Graph} directly, to ensure consistent {@link #equals(Object)} + * and {@link #hashCode()} results across different graph implementations. + * + * @author James Sexton + * @param Node parameter type + * @param Edge parameter type + */ +public abstract class AbstractGraph implements Graph { + + protected final GraphConfig config; + + /** + * Not all subclasses necessarily need to expose a constructor that takes a {@link GraphConfig}, + * but they do need to provide the {@code config} here that will be returned by {@link #config()}. + */ + protected AbstractGraph(GraphConfig config) { + this.config = config; + } + + @Override + public GraphConfig config() { + return config; + } + + @Override + public long degree(Object node) { + return incidentEdges(node).size(); + } + + @Override + public long inDegree(Object node) { + return inEdges(node).size(); + } + + @Override + public long outDegree(Object node) { + return outEdges(node).size(); + } + + @Override + public boolean equals(@Nullable Object object) { + if (!(object instanceof Graph)) { + return false; + } + return Graphs.equal(this, (Graph) object); + } + + @Override + public int hashCode() { + return Graphs.hashCode(this); + } + + @Override + public String toString() { + return Graphs.toString(this); + } +} diff --git a/guava/src/com/google/common/graph/AbstractImmutableGraph.java b/guava/src/com/google/common/graph/AbstractImmutableGraph.java index c3d8fb60be76..9d366c1b6ec5 100644 --- a/guava/src/com/google/common/graph/AbstractImmutableGraph.java +++ b/guava/src/com/google/common/graph/AbstractImmutableGraph.java @@ -25,7 +25,11 @@ * @param Node parameter type * @param Edge parameter type */ -abstract class AbstractImmutableGraph implements Graph { +abstract class AbstractImmutableGraph extends AbstractGraph { + + AbstractImmutableGraph(GraphConfig config) { + super(config); + } @Override public boolean addNode(N n) { diff --git a/guava/src/com/google/common/graph/Graph.java b/guava/src/com/google/common/graph/Graph.java index b3960a9df3c3..674077a41381 100644 --- a/guava/src/com/google/common/graph/Graph.java +++ b/guava/src/com/google/common/graph/Graph.java @@ -156,6 +156,8 @@ * * Note that (1) and (2) are generally preferred. (5) is generally a hazardous design choice * and should be avoided, because keeping the internal data structures consistent can be tricky. + *
  • Prefer extending {@link AbstractGraph} over implementing {@link Graph} directly. This will + * ensure consistent {@link #equals(Object)} and {@link #hashCode()} across implementations. *
  • {@code Multimap}s are not sufficient internal data structures for Graph implementations * that support isolated nodes (nodes that have no incident edges), due to their restriction * that a key either maps to at least one value, or is not present in the {@code Multimap}. @@ -393,19 +395,16 @@ public interface Graph { boolean removeEdge(Object edge); /** - * Returns {@code true} iff {@code object} is the same type of graph (directed, undirected, - * hypergraph) as this graph, and the same node/edge relationships exist in both graphs. + * Returns {@code true} iff {@code object} is a graph that has the same node/edge relationships + * as those in this graph. * *

    Thus, two graphs A and B are equal if all of the following are true: *

      - *
    • A and B are of the same type ({@code DirectedGraph, UndirectedGraph, Hypergraph}) *
    • A and B have the same node set *
    • A and B have the same edge set *
    • A and B have the same incidence relationships, e.g., for each node/edge in A and in B * its incident edge/node set in A is the same as its incident edge/node set in B. - *
      Thus, even if a {@code node} has the same sets of adjacent nodes - * (neighbors) in both A and B, if the sets of edges by which {@code node} is connected to - * its adjacent nodes are not the same in both A and B, then A and B are not equal. + *
      Thus, every edge in A and B connect the same nodes in the same direction (if any). *
    * *

    Properties that are not respected by this method: @@ -416,7 +415,21 @@ public interface Graph { *

  • Edge/node ordering. The order in which edges or nodes are added to the graph, and the * order in which they are iterated over, are irrelevant. * + * + *

    A reference implementation of this is provided by {@link Graphs#equal(Graph, Graph)}. */ @Override boolean equals(@Nullable Object object); + + /** + * Returns the hash code for this graph. The hash code of a graph is defined as the hash code + * of a map from each of the graph's nodes to their incident edges. + * + *

    A reference implementation of this is provided by {@link Graphs#hashCode(Graph)}. + * + *

    Note that by this definition, two graphs that are equal in every aspect except edge + * direction will have the same hash code (but can still be differentiated by {@link #equals}. + */ + @Override + int hashCode(); } diff --git a/guava/src/com/google/common/graph/Graphs.java b/guava/src/com/google/common/graph/Graphs.java index 3eefeb85731a..7a11d7eca7b6 100644 --- a/guava/src/com/google/common/graph/Graphs.java +++ b/guava/src/com/google/common/graph/Graphs.java @@ -29,6 +29,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.Iterator; +import java.util.Map; import java.util.Set; import javax.annotation.Nullable; @@ -379,13 +380,11 @@ public static UndirectedGraph createUndirected(GraphConfig config) } /** - * Returns true iff {@code graph1} and {@code graph2} have the same node and edge sets and - * each edge has the same source and target in both graphs. + * Returns true iff {@code graph1} and {@code graph2} have the same node/edge relationships. * * @see Graph#equals(Object) */ - public static boolean equal( - @Nullable DirectedGraph graph1, @Nullable DirectedGraph graph2) { + public static boolean equal(@Nullable Graph graph1, @Nullable Graph graph2) { if (graph1 == graph2) { return true; } @@ -394,76 +393,45 @@ public static boolean equal( return false; } - if (!graph1.nodes().equals(graph2.nodes()) || !graph1.edges().equals(graph2.edges())) { + if (graph1.edges().size() != graph2.edges().size()) { return false; } - for (Object edge : graph1.edges()) { - if (!graph1.source(edge).equals(graph2.source(edge)) - || !graph1.target(edge).equals(graph2.target(edge))) { - return false; - } - } - return true; - } - - /** - * Returns true iff {@code graph1} and {@code graph2} have the same node and edge sets and - * each edge has the same incident node set in both graphs. - * - * @see Graph#equals(Object) - */ - public static boolean equal(@Nullable Graph graph1, @Nullable Graph graph2) { - if (graph1 == graph2) { - return true; - } - - if (graph1 == null || graph2 == null) { + if (!graph1.nodes().equals(graph2.nodes())) { return false; } - if (!graph1.nodes().equals(graph2.nodes()) || !graph1.edges().equals(graph2.edges())) { - return false; - } - - for (Object edge : graph1.edges()) { - if (!graph1.incidentNodes(edge).equals(graph2.incidentNodes(edge))) { + for (Object node : graph1.nodes()) { + if (!graph1.inEdges(node).equals(graph2.inEdges(node))) { + return false; + } + // TODO(b/27195992): Consider an optimization for the case where both graphs are undirected. + if (!graph1.outEdges(node).equals(graph2.outEdges(node))) { return false; } } + return true; } /** - * Returns a string representation of {@code graph}, encoding the direction of each edge. + * Returns the hash code of {@code graph}. + * + * @see Graph#hashCode() */ - public static String toString(final DirectedGraph graph) { - Function edgeToEndpoints = new Function() { - @Override - public String apply(Object edge) { - return String.format("<%s -> %s>", graph.source(edge), graph.target(edge)); - } - }; - return String.format("config: %s, nodes: %s, edges: %s", - graph.config(), - graph.nodes(), - Maps.asMap(graph.edges(), edgeToEndpoints)); + public static int hashCode(Graph graph) { + return nodeToIncidentEdges(graph).hashCode(); } /** - * Returns a string representation of {@code graph}, without regard to direction of edges. + * Returns a string representation of {@code graph}. Encodes edge direction if {@code graph} + * is a {@link DirectedGraph}. */ - public static String toString(final Graph graph) { - Function edgeToIncidentNodes = new Function() { - @Override - public String apply(Object edge) { - return graph.incidentNodes(edge).toString(); - } - }; + public static String toString(Graph graph) { return String.format("config: %s, nodes: %s, edges: %s", graph.config(), graph.nodes(), - Maps.asMap(graph.edges(), edgeToIncidentNodes)); + Maps.asMap(graph.edges(), edgeToIncidentNodesString(graph))); } /** @@ -481,4 +449,43 @@ public boolean apply(E edge) { } }; } + + /** + * Returns a map that is a live view of {@code graph}, with nodes as keys + * and the set of incident edges as values. + */ + private static Map> nodeToIncidentEdges(final Graph graph) { + checkNotNull(graph, "graph"); + return Maps.asMap(graph.nodes(), new Function>() { + @Override + public Set apply(N node) { + return graph.incidentEdges(node); + } + }); + } + + /** + * Returns a function that transforms an edge into a string representation of its incident nodes + * in {@code graph}. The function's {@code apply} method will throw an + * {@link IllegalArgumentException} if {@code graph} does not contain {@code edge}. + */ + private static Function edgeToIncidentNodesString(final Graph graph) { + if (graph instanceof DirectedGraph) { + @SuppressWarnings("unchecked") + final DirectedGraph directedGraph = (DirectedGraph) graph; + return new Function() { + @Override + public String apply(Object edge) { + return String.format("<%s -> %s>", + directedGraph.source(edge), directedGraph.target(edge)); + } + }; + } + return new Function() { + @Override + public String apply(Object edge) { + return graph.incidentNodes(edge).toString(); + } + }; + } } diff --git a/guava/src/com/google/common/graph/ImmutableDirectedGraph.java b/guava/src/com/google/common/graph/ImmutableDirectedGraph.java index 5e6b2316d26f..a7d37d5c181e 100644 --- a/guava/src/com/google/common/graph/ImmutableDirectedGraph.java +++ b/guava/src/com/google/common/graph/ImmutableDirectedGraph.java @@ -22,7 +22,6 @@ import static com.google.common.graph.GraphErrorMessageUtils.NODE_NOT_IN_GRAPH; import com.google.common.annotations.Beta; -import com.google.common.base.Objects; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; @@ -30,8 +29,6 @@ import java.util.Set; -import javax.annotation.Nullable; - /** * Implementation of an immutable directed graph consisting of nodes * of type N and edges of type E. @@ -78,9 +75,9 @@ public final class ImmutableDirectedGraph extends AbstractImmutableGraph> nodeConnections; // All edges in the graph exist in this map private final ImmutableMap> edgeToIncidentNodes; - private final GraphConfig config; private ImmutableDirectedGraph(Builder builder) { + super(builder.directedGraph.config()); DirectedGraph directedGraph = builder.directedGraph; ImmutableMap.Builder> nodeConnectionsBuilder = ImmutableMap.builder(); @@ -98,7 +95,6 @@ private ImmutableDirectedGraph(Builder builder) { edgeToIncidentNodesBuilder.put(edge, incidentNodes); } this.edgeToIncidentNodes = edgeToIncidentNodesBuilder.build(); - this.config = directedGraph.config(); } @Override @@ -111,11 +107,6 @@ public Set edges() { return edgeToIncidentNodes.keySet(); } - @Override - public GraphConfig config() { - return config; - } - @Override public Set incidentEdges(Object node) { return checkedConnections(node).incidentEdges(); @@ -183,21 +174,6 @@ public Set successors(Object node) { return checkedConnections(node).successors(); } - @Override - public long degree(Object node) { - return incidentEdges(node).size(); - } - - @Override - public long inDegree(Object node) { - return inEdges(node).size(); - } - - @Override - public long outDegree(Object node) { - return outEdges(node).size(); - } - @Override public N source(Object edge) { return checkedIncidentNodes(edge).node1(); @@ -208,22 +184,6 @@ public N target(Object edge) { return checkedIncidentNodes(edge).node2(); } - @Override - public boolean equals(@Nullable Object object) { - return (object instanceof DirectedGraph) && Graphs.equal(this, (DirectedGraph) object); - } - - @Override - public int hashCode() { - // The node set is included in the hash to differentiate between graphs with isolated nodes. - return Objects.hashCode(nodes(), edgeToIncidentNodes); - } - - @Override - public String toString() { - return Graphs.toString(this); - } - private NodeConnections checkedConnections(Object node) { checkNotNull(node, "node"); NodeConnections connections = nodeConnections.get(node); diff --git a/guava/src/com/google/common/graph/ImmutableUndirectedGraph.java b/guava/src/com/google/common/graph/ImmutableUndirectedGraph.java index 066b62ad519d..4c3e41e0a4fd 100644 --- a/guava/src/com/google/common/graph/ImmutableUndirectedGraph.java +++ b/guava/src/com/google/common/graph/ImmutableUndirectedGraph.java @@ -22,7 +22,6 @@ import static com.google.common.graph.GraphErrorMessageUtils.NODE_NOT_IN_GRAPH; import com.google.common.annotations.Beta; -import com.google.common.base.Objects; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -31,8 +30,6 @@ import java.util.Set; -import javax.annotation.Nullable; - /** * Implementation of an immutable undirected graph consisting of nodes of type N * and edges of type E. @@ -70,13 +67,14 @@ @Beta public final class ImmutableUndirectedGraph extends AbstractImmutableGraph implements UndirectedGraph { + // All nodes in the graph exist in this map private final ImmutableMap> nodeConnections; // All edges in the graph exist in this map private final ImmutableMap> edgeToIncidentNodes; - private final GraphConfig config; private ImmutableUndirectedGraph(Builder builder) { + super(builder.undirectedGraph.config()); UndirectedGraph undirectedGraph = builder.undirectedGraph; ImmutableMap.Builder> nodeConnectionsBuilder = ImmutableMap.builder(); for (N node : undirectedGraph.nodes()) { @@ -89,7 +87,6 @@ private ImmutableUndirectedGraph(Builder builder) { edgeToNodesBuilder.put(edge, IncidentNodes.of(undirectedGraph.incidentNodes(edge))); } this.edgeToIncidentNodes = edgeToNodesBuilder.build(); - this.config = undirectedGraph.config(); } @Override @@ -102,11 +99,6 @@ public Set edges() { return edgeToIncidentNodes.keySet(); } - @Override - public GraphConfig config() { - return config; - } - @Override public Set incidentEdges(Object node) { return checkedConnections(node).incidentEdges(); @@ -178,38 +170,6 @@ public Set successors(Object node) { return adjacentNodes(node); } - @Override - public long degree(Object node) { - return incidentEdges(node).size(); - } - - @Override - public long inDegree(Object node) { - return degree(node); - } - - @Override - public long outDegree(Object node) { - return degree(node); - } - - @Override - public boolean equals(@Nullable Object object) { - return (object instanceof UndirectedGraph) - && Graphs.equal(this, (UndirectedGraph) object); - } - - @Override - public int hashCode() { - // The node set is included in the hash to differentiate between graphs with isolated nodes. - return Objects.hashCode(nodes(), edgeToIncidentNodes); - } - - @Override - public String toString() { - return Graphs.toString(this); - } - private NodeConnections checkedConnections(Object node) { checkNotNull(node, "node"); NodeConnections connections = nodeConnections.get(node); diff --git a/guava/src/com/google/common/graph/IncidenceSetDirectedGraph.java b/guava/src/com/google/common/graph/IncidenceSetDirectedGraph.java index d643aa9c6503..f5f94786094c 100644 --- a/guava/src/com/google/common/graph/IncidenceSetDirectedGraph.java +++ b/guava/src/com/google/common/graph/IncidenceSetDirectedGraph.java @@ -24,7 +24,6 @@ import static com.google.common.graph.GraphErrorMessageUtils.REUSING_EDGE; import static com.google.common.graph.GraphErrorMessageUtils.SELF_LOOPS_NOT_ALLOWED; -import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; @@ -36,8 +35,6 @@ import java.util.Map; import java.util.Set; -import javax.annotation.Nullable; - /** * Adjacency-set-based implementation of a directed graph consisting of nodes * of type N and edges of type E. @@ -103,20 +100,20 @@ * @see IncidenceSetUndirectedGraph * @see Graphs */ -final class IncidenceSetDirectedGraph implements DirectedGraph { +final class IncidenceSetDirectedGraph extends AbstractGraph + implements DirectedGraph { // TODO(b/24620028): Enable this class to support sorted nodes/edges. private final Map> nodeConnections; private final Map> edgeToIncidentNodes; - private final GraphConfig config; IncidenceSetDirectedGraph(GraphConfig config) { + super(config); // The default of 11 is rather arbitrary, but roughly matches the sizing of just new HashMap() this.nodeConnections = Maps.newLinkedHashMapWithExpectedSize(config.getExpectedNodeCount().or(11)); this.edgeToIncidentNodes = Maps.newLinkedHashMapWithExpectedSize(config.getExpectedEdgeCount().or(11)); - this.config = config; } @Override @@ -129,11 +126,6 @@ public Set edges() { return Collections.unmodifiableSet(edgeToIncidentNodes.keySet()); } - @Override - public GraphConfig config() { - return config; - } - @Override public Set incidentEdges(Object node) { return checkedConnections(node).incidentEdges(); @@ -199,21 +191,6 @@ public Set successors(Object node) { return checkedConnections(node).successors(); } - @Override - public long degree(Object node) { - return incidentEdges(node).size(); - } - - @Override - public long inDegree(Object node) { - return inEdges(node).size(); - } - - @Override - public long outDegree(Object node) { - return outEdges(node).size(); - } - @Override public N source(Object edge) { return checkedIncidentNodes(edge).node1(); @@ -336,22 +313,6 @@ private void removeEdgeAndUpdateConnections(Object edge, boolean disconnectIncid edgeToIncidentNodes.remove(edge); } - @Override - public boolean equals(@Nullable Object other) { - return (other instanceof DirectedGraph) && Graphs.equal(this, (DirectedGraph) other); - } - - @Override - public int hashCode() { - // The node set is included in the hash to differentiate between graphs with isolated nodes. - return Objects.hashCode(nodes(), edgeToIncidentNodes); - } - - @Override - public String toString() { - return Graphs.toString(this); - } - private NodeConnections checkedConnections(Object node) { checkNotNull(node, "node"); NodeConnections connections = nodeConnections.get(node); diff --git a/guava/src/com/google/common/graph/IncidenceSetUndirectedGraph.java b/guava/src/com/google/common/graph/IncidenceSetUndirectedGraph.java index 8d123c89b0c4..06f56cb0e2f2 100644 --- a/guava/src/com/google/common/graph/IncidenceSetUndirectedGraph.java +++ b/guava/src/com/google/common/graph/IncidenceSetUndirectedGraph.java @@ -24,7 +24,6 @@ import static com.google.common.graph.GraphErrorMessageUtils.REUSING_EDGE; import static com.google.common.graph.GraphErrorMessageUtils.SELF_LOOPS_NOT_ALLOWED; -import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; @@ -36,8 +35,6 @@ import java.util.Map; import java.util.Set; -import javax.annotation.Nullable; - /** * Adjacency-set-based implementation of an undirected graph consisting of nodes * of type N and edges of type E. @@ -98,22 +95,22 @@ * @see IncidenceSetDirectedGraph * @see Graphs */ -final class IncidenceSetUndirectedGraph implements UndirectedGraph { +final class IncidenceSetUndirectedGraph extends AbstractGraph + implements UndirectedGraph { // TODO(b/24620028): Enable this class to support sorted nodes/edges. // All nodes in the graph exist in this map private final Map> nodeConnections; // All edges in the graph exist in this map private final Map> edgeToIncidentNodes; - private final GraphConfig config; IncidenceSetUndirectedGraph(GraphConfig config) { + super(config); // The default of 11 is rather arbitrary, but roughly matches the sizing of just new HashMap() this.nodeConnections = Maps.newLinkedHashMapWithExpectedSize(config.getExpectedNodeCount().or(11)); this.edgeToIncidentNodes = Maps.newLinkedHashMapWithExpectedSize(config.getExpectedEdgeCount().or(11)); - this.config = config; } @Override @@ -126,11 +123,6 @@ public Set edges() { return Collections.unmodifiableSet(edgeToIncidentNodes.keySet()); } - @Override - public GraphConfig config() { - return config; - } - @Override public Set incidentEdges(Object node) { return checkedConnections(node).incidentEdges(); @@ -201,21 +193,6 @@ public Set successors(Object node) { return adjacentNodes(node); } - @Override - public long degree(Object node) { - return incidentEdges(node).size(); - } - - @Override - public long inDegree(Object node) { - return degree(node); - } - - @Override - public long outDegree(Object node) { - return degree(node); - } - // Element Mutation @Override @@ -314,23 +291,6 @@ public boolean removeEdge(Object edge) { return true; } - @Override - public boolean equals(@Nullable Object object) { - return (object instanceof UndirectedGraph) - && Graphs.equal(this, (UndirectedGraph) object); - } - - @Override - public int hashCode() { - // The node set is included in the hash to differentiate between graphs with isolated nodes. - return Objects.hashCode(nodes(), edgeToIncidentNodes); - } - - @Override - public String toString() { - return Graphs.toString(this); - } - private NodeConnections checkedConnections(Object node) { checkNotNull(node, "node"); NodeConnections connections = nodeConnections.get(node);