Skip to content

Commit 9507996

Browse files
ineuwirthGoogle Java Core Libraries
authored and
Google Java Core Libraries
committedOct 6, 2023
fix behavior of Graph/ValueGraph views for a node when that node is removed from the graph
RELNOTES=fix behavior of Graph/ValueGraph views for a node when that node is removed from the graph PiperOrigin-RevId: 571390607
1 parent f87f68c commit 9507996

File tree

6 files changed

+36
-18
lines changed

6 files changed

+36
-18
lines changed
 

‎android/guava-tests/test/com/google/common/graph/AbstractGraphTest.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -385,10 +385,13 @@ public void removeNode_nodeNotPresent() {
385385
public void removeNode_queryAfterRemoval() {
386386
assume().that(graphIsMutable()).isTrue();
387387

388-
addNode(N1);
389-
@SuppressWarnings("unused")
390-
Set<Integer> unused = graph.adjacentNodes(N1); // ensure cache (if any) is populated
388+
putEdge(N1, N2);
389+
putEdge(N2, N1);
390+
Set<Integer> n1AdjacentNodes = graph.adjacentNodes(N1);
391+
Set<Integer> n2AdjacentNodes = graph.adjacentNodes(N2);
391392
assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
393+
assertThat(n1AdjacentNodes).isEmpty();
394+
assertThat(n2AdjacentNodes).isEmpty();
392395
IllegalArgumentException e =
393396
assertThrows(IllegalArgumentException.class, () -> graph.adjacentNodes(N1));
394397
assertNodeNotInGraphErrorMessage(e);

‎android/guava-tests/test/com/google/common/graph/AbstractNetworkTest.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -670,11 +670,12 @@ public void removeNode_nodeNotPresent() {
670670
public void removeNode_queryAfterRemoval() {
671671
assume().that(graphIsMutable()).isTrue();
672672

673-
addNode(N1);
674-
@SuppressWarnings("unused")
675-
Set<Integer> unused =
676-
networkAsMutableNetwork.adjacentNodes(N1); // ensure cache (if any) is populated
673+
addEdge(N1, N2, E12);
674+
Set<Integer> n1AdjacentNodes = networkAsMutableNetwork.adjacentNodes(N1);
675+
Set<Integer> n2AdjacentNodes = networkAsMutableNetwork.adjacentNodes(N2);
677676
assertTrue(networkAsMutableNetwork.removeNode(N1));
677+
assertThat(n1AdjacentNodes).isEmpty();
678+
assertThat(n2AdjacentNodes).isEmpty();
678679
IllegalArgumentException e =
679680
assertThrows(
680681
IllegalArgumentException.class, () -> networkAsMutableNetwork.adjacentNodes(N1));

‎android/guava/src/com/google/common/graph/StandardMutableValueGraph.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import static com.google.common.graph.Graphs.checkPositive;
2525
import static java.util.Objects.requireNonNull;
2626

27+
import com.google.common.collect.ImmutableList;
2728
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2829
import javax.annotation.CheckForNull;
2930

@@ -136,17 +137,21 @@ public boolean removeNode(N node) {
136137
}
137138
}
138139

139-
for (N successor : connections.successors()) {
140+
for (N successor : ImmutableList.copyOf(connections.successors())) {
140141
// requireNonNull is safe because the node is a successor.
141142
requireNonNull(nodeConnections.getWithoutCaching(successor)).removePredecessor(node);
143+
requireNonNull(connections.removeSuccessor(successor));
142144
--edgeCount;
143145
}
144146
if (isDirected()) { // In undirected graphs, the successor and predecessor sets are equal.
145-
for (N predecessor : connections.predecessors()) {
147+
// Since views are returned, we need to copy the predecessors that will be removed.
148+
// Thus we avoid modifying the underlying view while iterating over it.
149+
for (N predecessor : ImmutableList.copyOf(connections.predecessors())) {
146150
// requireNonNull is safe because the node is a predecessor.
147151
checkState(
148152
requireNonNull(nodeConnections.getWithoutCaching(predecessor)).removeSuccessor(node)
149153
!= null);
154+
connections.removePredecessor(predecessor);
150155
--edgeCount;
151156
}
152157
}

‎guava-tests/test/com/google/common/graph/AbstractGraphTest.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -385,10 +385,13 @@ public void removeNode_nodeNotPresent() {
385385
public void removeNode_queryAfterRemoval() {
386386
assume().that(graphIsMutable()).isTrue();
387387

388-
addNode(N1);
389-
@SuppressWarnings("unused")
390-
Set<Integer> unused = graph.adjacentNodes(N1); // ensure cache (if any) is populated
388+
putEdge(N1, N2);
389+
putEdge(N2, N1);
390+
Set<Integer> n1AdjacentNodes = graph.adjacentNodes(N1);
391+
Set<Integer> n2AdjacentNodes = graph.adjacentNodes(N2);
391392
assertThat(graphAsMutableGraph.removeNode(N1)).isTrue();
393+
assertThat(n1AdjacentNodes).isEmpty();
394+
assertThat(n2AdjacentNodes).isEmpty();
392395
IllegalArgumentException e =
393396
assertThrows(IllegalArgumentException.class, () -> graph.adjacentNodes(N1));
394397
assertNodeNotInGraphErrorMessage(e);

‎guava-tests/test/com/google/common/graph/AbstractNetworkTest.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -677,11 +677,12 @@ public void removeNode_nodeNotPresent() {
677677
public void removeNode_queryAfterRemoval() {
678678
assume().that(graphIsMutable()).isTrue();
679679

680-
addNode(N1);
681-
@SuppressWarnings("unused")
682-
Set<Integer> unused =
683-
networkAsMutableNetwork.adjacentNodes(N1); // ensure cache (if any) is populated
680+
addEdge(N1, N2, E12);
681+
Set<Integer> n1AdjacentNodes = networkAsMutableNetwork.adjacentNodes(N1);
682+
Set<Integer> n2AdjacentNodes = networkAsMutableNetwork.adjacentNodes(N2);
684683
assertTrue(networkAsMutableNetwork.removeNode(N1));
684+
assertThat(n1AdjacentNodes).isEmpty();
685+
assertThat(n2AdjacentNodes).isEmpty();
685686
IllegalArgumentException e =
686687
assertThrows(
687688
IllegalArgumentException.class, () -> networkAsMutableNetwork.adjacentNodes(N1));

‎guava/src/com/google/common/graph/StandardMutableValueGraph.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import static com.google.common.graph.Graphs.checkPositive;
2525
import static java.util.Objects.requireNonNull;
2626

27+
import com.google.common.collect.ImmutableList;
2728
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2829
import javax.annotation.CheckForNull;
2930

@@ -136,17 +137,21 @@ public boolean removeNode(N node) {
136137
}
137138
}
138139

139-
for (N successor : connections.successors()) {
140+
for (N successor : ImmutableList.copyOf(connections.successors())) {
140141
// requireNonNull is safe because the node is a successor.
141142
requireNonNull(nodeConnections.getWithoutCaching(successor)).removePredecessor(node);
143+
requireNonNull(connections.removeSuccessor(successor));
142144
--edgeCount;
143145
}
144146
if (isDirected()) { // In undirected graphs, the successor and predecessor sets are equal.
145-
for (N predecessor : connections.predecessors()) {
147+
// Since views are returned, we need to copy the predecessors that will be removed.
148+
// Thus we avoid modifying the underlying view while iterating over it.
149+
for (N predecessor : ImmutableList.copyOf(connections.predecessors())) {
146150
// requireNonNull is safe because the node is a predecessor.
147151
checkState(
148152
requireNonNull(nodeConnections.getWithoutCaching(predecessor)).removeSuccessor(node)
149153
!= null);
154+
connections.removePredecessor(predecessor);
150155
--edgeCount;
151156
}
152157
}

0 commit comments

Comments
 (0)
Please sign in to comment.