Skip to content

Commit

Permalink
fix behavior of Graph/ValueGraph views for a node when that node is r…
Browse files Browse the repository at this point in the history
…emoved from the graph

RELNOTES=fix behavior of Graph/ValueGraph views for a node when that node is removed from the graph
PiperOrigin-RevId: 571390607
  • Loading branch information
ineuwirth authored and Google Java Core Libraries committed Oct 6, 2023
1 parent f87f68c commit 9507996
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -385,10 +385,13 @@ public void removeNode_nodeNotPresent() {
public void removeNode_queryAfterRemoval() {
assume().that(graphIsMutable()).isTrue();

addNode(N1);
@SuppressWarnings("unused")
Set<Integer> unused = graph.adjacentNodes(N1); // ensure cache (if any) is populated
putEdge(N1, N2);
putEdge(N2, N1);
Set<Integer> n1AdjacentNodes = graph.adjacentNodes(N1);
Set<Integer> n2AdjacentNodes = graph.adjacentNodes(N2);
assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
assertThat(n1AdjacentNodes).isEmpty();
assertThat(n2AdjacentNodes).isEmpty();
IllegalArgumentException e =
assertThrows(IllegalArgumentException.class, () -> graph.adjacentNodes(N1));
assertNodeNotInGraphErrorMessage(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -670,11 +670,12 @@ public void removeNode_nodeNotPresent() {
public void removeNode_queryAfterRemoval() {
assume().that(graphIsMutable()).isTrue();

addNode(N1);
@SuppressWarnings("unused")
Set<Integer> unused =
networkAsMutableNetwork.adjacentNodes(N1); // ensure cache (if any) is populated
addEdge(N1, N2, E12);
Set<Integer> n1AdjacentNodes = networkAsMutableNetwork.adjacentNodes(N1);
Set<Integer> n2AdjacentNodes = networkAsMutableNetwork.adjacentNodes(N2);
assertTrue(networkAsMutableNetwork.removeNode(N1));
assertThat(n1AdjacentNodes).isEmpty();
assertThat(n2AdjacentNodes).isEmpty();
IllegalArgumentException e =
assertThrows(
IllegalArgumentException.class, () -> networkAsMutableNetwork.adjacentNodes(N1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import static com.google.common.graph.Graphs.checkPositive;
import static java.util.Objects.requireNonNull;

import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import javax.annotation.CheckForNull;

Expand Down Expand Up @@ -136,17 +137,21 @@ public boolean removeNode(N node) {
}
}

for (N successor : connections.successors()) {
for (N successor : ImmutableList.copyOf(connections.successors())) {
// requireNonNull is safe because the node is a successor.
requireNonNull(nodeConnections.getWithoutCaching(successor)).removePredecessor(node);
requireNonNull(connections.removeSuccessor(successor));
--edgeCount;
}
if (isDirected()) { // In undirected graphs, the successor and predecessor sets are equal.
for (N predecessor : connections.predecessors()) {
// Since views are returned, we need to copy the predecessors that will be removed.
// Thus we avoid modifying the underlying view while iterating over it.
for (N predecessor : ImmutableList.copyOf(connections.predecessors())) {
// requireNonNull is safe because the node is a predecessor.
checkState(
requireNonNull(nodeConnections.getWithoutCaching(predecessor)).removeSuccessor(node)
!= null);
connections.removePredecessor(predecessor);
--edgeCount;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,10 +385,13 @@ public void removeNode_nodeNotPresent() {
public void removeNode_queryAfterRemoval() {
assume().that(graphIsMutable()).isTrue();

addNode(N1);
@SuppressWarnings("unused")
Set<Integer> unused = graph.adjacentNodes(N1); // ensure cache (if any) is populated
putEdge(N1, N2);
putEdge(N2, N1);
Set<Integer> n1AdjacentNodes = graph.adjacentNodes(N1);
Set<Integer> n2AdjacentNodes = graph.adjacentNodes(N2);
assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
assertThat(n1AdjacentNodes).isEmpty();
assertThat(n2AdjacentNodes).isEmpty();
IllegalArgumentException e =
assertThrows(IllegalArgumentException.class, () -> graph.adjacentNodes(N1));
assertNodeNotInGraphErrorMessage(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -677,11 +677,12 @@ public void removeNode_nodeNotPresent() {
public void removeNode_queryAfterRemoval() {
assume().that(graphIsMutable()).isTrue();

addNode(N1);
@SuppressWarnings("unused")
Set<Integer> unused =
networkAsMutableNetwork.adjacentNodes(N1); // ensure cache (if any) is populated
addEdge(N1, N2, E12);
Set<Integer> n1AdjacentNodes = networkAsMutableNetwork.adjacentNodes(N1);
Set<Integer> n2AdjacentNodes = networkAsMutableNetwork.adjacentNodes(N2);
assertTrue(networkAsMutableNetwork.removeNode(N1));
assertThat(n1AdjacentNodes).isEmpty();
assertThat(n2AdjacentNodes).isEmpty();
IllegalArgumentException e =
assertThrows(
IllegalArgumentException.class, () -> networkAsMutableNetwork.adjacentNodes(N1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import static com.google.common.graph.Graphs.checkPositive;
import static java.util.Objects.requireNonNull;

import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import javax.annotation.CheckForNull;

Expand Down Expand Up @@ -136,17 +137,21 @@ public boolean removeNode(N node) {
}
}

for (N successor : connections.successors()) {
for (N successor : ImmutableList.copyOf(connections.successors())) {
// requireNonNull is safe because the node is a successor.
requireNonNull(nodeConnections.getWithoutCaching(successor)).removePredecessor(node);
requireNonNull(connections.removeSuccessor(successor));
--edgeCount;
}
if (isDirected()) { // In undirected graphs, the successor and predecessor sets are equal.
for (N predecessor : connections.predecessors()) {
// Since views are returned, we need to copy the predecessors that will be removed.
// Thus we avoid modifying the underlying view while iterating over it.
for (N predecessor : ImmutableList.copyOf(connections.predecessors())) {
// requireNonNull is safe because the node is a predecessor.
checkState(
requireNonNull(nodeConnections.getWithoutCaching(predecessor)).removeSuccessor(node)
!= null);
connections.removePredecessor(predecessor);
--edgeCount;
}
}
Expand Down

0 comments on commit 9507996

Please sign in to comment.