guava
guava copied to clipboard
Tables.computeIfAbsent
Issue #902 requests Multitable, which we have been hesitant to add because of complexity.
When the JDK was hesitant to add even Multimap and Multiset, they added Map.computeIfAbsent, which serves many of the same use cases noticeably less well but with a significantly smaller API footprint. Perhaps we could do the same here? We could wait for Java 8 to add a true default method, or we could provide it as a static method.
Thought I'd give my 2 cents on this proverbial bikeshed. :)
Given how Google's recently migrated to Java 8 (https://github.com/google/guava/issues/2443#issuecomment-248103813), I think it's worth waiting until Guava 21 is in development to allow Table.computeIfAbsent to be implemented as a default method.
You may safely assume now is a good time for us to be making these decisions. We have Java 8, Guava 21 will hopefully follow swiftly on the heels of 20 since we already have all the Java 8 stuff internally, and as usual, we often test things out for a while internally before releasing them into Guava.
Now is a good time to discuss this. :)
Great! In that case, if this has not yet been decided on internally, I'd feel strongly about making Table.computeIfAbsent a default method - it would have increased "discoverability" in IDEs, since one could type e.g. fooTable. and they'd see computeIfAbsent as a potential option.
I think at the moment the question isn't whether it should be a default method, but whether it should be provided at all?
@lowasser I get the impression from #902 and #1227 that there is real demand for a method like this or a Multitable class (but I don't know how much).
In particular, I find this comment regarding Bigtable very interesting. It suggests to me that even if Multitables are hardly useful when creating "low level" software (like command line programs), they are a lot more useful when creating more "high level" stuff like, say, Big Data applications.
At this point, it's not clear to me what other questions we should ask ourselves to determine the usefulness of Table.computeIfAbsent/Multitable in Guava itself...
"How much demand is there" is the question we'd want to ask, it means we need to do research on Google's codebase to see how many people are using common workarounds for the nonexistence of this method.
Okay, that's obviously something I can't do, as I'm not a Google employee, so all I can do at this point is look forward to further internal progress on this. :)
Very rough searches: ~6% of Table usages inside Google are either a Table<..., ..., List> or Table<..., ..., Set>. Compare to ~8% of Map usages with List/Set values (despite the existence of Multimap, which is itself ~13% as popular as Map).
(And of course, users may be using different workarounds -- Map<R, Map<C, List<V>>, Multimap<R, Pair<C, List<V>>, or whatever.)
If anyone wants to dig deeper, I suggest a search along the lines of:
\bTable<[^(=]*,\s*(List|Set)<.*>> case:yes lang:java
Just to add another possibly-complicating factor, at least some of the Table uses inside Google may become Graphs/ValueGraphs/Networks now that common.graph is available and becoming more mainstream. (One of the current use cases that's described for Table is, in fact, for representing a graph: https://github.com/google/guava/wiki/NewCollectionTypesExplained#table)
This suggests that we may want to consider (down the road, at least) improved support for Collection-based Values in a ValueGraph. (Similar to Map<K, Collection<V>>, one can already do ValueGraph<N, Collection<V>>, but facilitating working with that kind of data is what Multimap is for.)
@Bezier89 FYI
FWIW, ValueGraph has two existing methods that (as far as I can tell) supports the requested need on the read side:
Optional<V> edgeValue(N nodeU, N nodeV); // use with orElse()
V edgeValueOrDefault(N nodeU, N nodeV);
But there's nothing on the write side, i.e., MutableValueGraph.putEdgeValueIfAbsent() does not exist. I could imagine adding it but so far no one's asked for it (in that context, at least).
Any chance of this becoming a thing? :smiley_cat:
Here is a static method you can use in the meantime:
/**
* If the specified {@code (rowKey, columnKey)} pair is not already associated with a value in the specified table
* (or is mapped to {@code null}), attempts to compute its value using the given mapping function
* and enters it into the table map unless {@code null}.
*
* @param rowKey row key that the computed value should be associated with
* @param columnKey column key that the computed value should be associated with
* @param mappingFunction the function to compute a value
*
* @return the current (existing or computed) value associated with
* the specified {@code (rowKey, columnKey)} pair, or {@code null} if the computed value is {@code null}
* @throws NullPointerException if the specified keys are null and
* the table does not support null keys, or the mappingFunction
* is null
* @throws IllegalArgumentException if the specified keys do not satisfy some constraint
* imposed by the the table implementation (e.g. {@link ArrayTable#put})
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
* @see Map#computeIfAbsent(Object, Function)
*/
public static <R, C, V> V computeIfAbsent(Table<R, C, V> table, R rowKey, C columnKey,
BiFunction<R, C, V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = table.get(rowKey, columnKey)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(rowKey, columnKey)) != null) {
table.put(rowKey, columnKey, newValue);
return newValue;
}
}
return v;
}
@cpovirk I suggest making this a default method in the Table interface, matching java.util.Map.computeIfAbsent