Skip to content

Graphs: immutable/unmodifiable operations should consistently throw UOE whether or not data would actually change #3909

@perceptron8

Description

@perceptron8

I know that may be several other options for the semantics besides mentioned at https://github.com/google/guava/wiki/GraphsExplained#accessor-behavior, and that "generally using" one of them is not a strict guarantee of any kind, nevertheless behavior, that I've encountered today, surprised me a lot.

Accessor behavior

For accessors that return a collection, there are several options for the semantics, including:

(...)
2. Collection is an unmodifiable view (e.g. Collections.unmodifiableSet()): attempts to modify the collection in any way will throw an exception [emphasis added], and modifications to the graph will be reflected in the collection.
(...)

(...); as of this writing, the built-in implementations generally use (2).

My general impression was that ImmutableGraph.nodes(), ImmutableGraph.edges() and other views should behave as if they were Collections.unmodifiableSet(). This is not always true as I will show.

@Test
public void emptyNodesAreUnmodifiable() {
	ImmutableGraph<?> graph = GraphBuilder.undirected().immutable().build();
	assertThrows(UnsupportedOperationException.class, () -> { graph.nodes().clear(); });
}

@Test
public void nonEmptyNodesAreUnmodifiable() {
	ImmutableGraph<?> graph = GraphBuilder.undirected().immutable().addNode(1).build();
	assertThrows(UnsupportedOperationException.class, () -> { graph.nodes().clear(); });
}

@Test
public void emptyEdgesAreUnmodifiable() {
	ImmutableGraph<?> graph = GraphBuilder.undirected().immutable().build();
	assertThrows(UnsupportedOperationException.class, () -> { graph.edges().clear(); });
}

@Test
public void nonEmptyEdgesAreUnmodifiable() {
	ImmutableGraph<?> graph = GraphBuilder.undirected().immutable().putEdge(1, 2).build();
	assertThrows(UnsupportedOperationException.class, () -> { graph.edges().clear(); });
}

Two of the above are failing: emptyNodesAreUnmodifiable() and emptyEdgesAreUnmodifiable().

Nothing happens when clear() is being invoked on empty view. I believe this is indeed an attempt to modify the collection, so UnsupportedOperationException should be thrown.

If you would like to make some changes, please remember about remove* and retainAll methods.


Probably related discussion on even stronger guarantees: #3480

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions