Use map.putIfAbsent() or map.computeIfAbsent() as appropriate instead of containsKey() + put()#7764
Conversation
Merging changes from master branch of apache/incubator-druid repo
|
Checking the failures. |
|
The test error looks potentially legitimate; please check this test and any others referenced in the Travis CI report: |
| <constraint name="x" maxCount="2147483647" within="" contains="" /> | ||
| <constraint name="ImmutableMap" regexp="Immutable.*" within="" contains="" /> | ||
| </searchConfiguration> | ||
| <searchConfiguration name="Use Map.putIfAbsent() instead of containsKey() + put()" created="1558868694225" text="if (!$m$.containsKey($k$)) { $m$.put($k$, $v$); }" recursive="false" caseInsensitive="true" type="JAVA"> |
There was a problem hiding this comment.
This isn't always good advice: in particular, if v is expensive to compute then computeIfAbsent should be used instead of putIfAbsent. This is because putIfAbsent computes v eagerly, whereas containsKey + put (or computeIfAbsent) computes v lazily. This can also have a deeper effect on behavior, if computation of v has side effects.
There was a problem hiding this comment.
After reviewing the rest of the patch, it seems like computeIfAbsent is appropriate more often than putIfAbsent, so the recommendation here should reflect that.
There was a problem hiding this comment.
Sure @gianm , I'll make the necessary changes.
There was a problem hiding this comment.
I've made the changes. Some of the changes are straight forward. I'll comment on those respective changes which are notable.
| if (!hyperLogLogs.containsKey(interval)) { | ||
| hyperLogLogs.put(interval, HyperLogLogCollector.makeLatestCollector()); | ||
| } | ||
| hyperLogLogs.putIfAbsent(interval, HyperLogLogCollector.makeLatestCollector()); |
There was a problem hiding this comment.
This is a good candidate for computeIfAbsent.
| if (!identifiersByDataSource.containsKey(identifier.getDataSource())) { | ||
| identifiersByDataSource.put(identifier.getDataSource(), new HashSet<>()); | ||
| } | ||
| identifiersByDataSource.putIfAbsent(identifier.getDataSource(), new HashSet<>()); |
There was a problem hiding this comment.
This is a good candidate for computeIfAbsent.
| if (!hllCollectors.containsKey(interval)) { | ||
| hllCollectors.put(interval, Optional.of(HyperLogLogCollector.makeLatestCollector())); | ||
| } | ||
| hllCollectors.putIfAbsent(interval, Optional.of(HyperLogLogCollector.makeLatestCollector())); |
There was a problem hiding this comment.
This is a good candidate for computeIfAbsent.
| if (!segmentFileMap.containsKey(segment)) { | ||
| segmentFileMap.put(segment, segmentLoader.getSegmentFiles(segment)); | ||
| } | ||
| segmentFileMap.putIfAbsent(segment, segmentLoader.getSegmentFiles(segment)); |
There was a problem hiding this comment.
This needs to be computeIfAbsent, since there are side effects.
| if (!serverExpectations.containsKey(lastServer)) { | ||
| serverExpectations.put(lastServer, new ServerExpectations(lastServer, makeMock(mocks, QueryRunner.class))); | ||
| } | ||
| serverExpectations.putIfAbsent(lastServer, new ServerExpectations(lastServer, makeMock(mocks, QueryRunner.class))); |
There was a problem hiding this comment.
Another good candidate for computeIfAbsent.
| if (!newContext.containsKey(PlannerContext.CTX_SQL_QUERY_ID)) { | ||
| newContext.put(PlannerContext.CTX_SQL_QUERY_ID, UUID.randomUUID().toString()); | ||
| } | ||
| newContext.putIfAbsent(PlannerContext.CTX_SQL_QUERY_ID, UUID.randomUUID().toString()); |
There was a problem hiding this comment.
Another good candidate for computeIfAbsent.
| if (!timelines.containsKey(descriptor.getDataSource())) { | ||
| timelines.put(descriptor.getDataSource(), new VersionedIntervalTimeline<>(Ordering.natural())); | ||
| } | ||
| timelines.putIfAbsent(descriptor.getDataSource(), new VersionedIntervalTimeline<>(Ordering.natural())); |
There was a problem hiding this comment.
Another good candidate for computeIfAbsent.
| <inspection_tool class="JSRedeclarationOfBlockScope" enabled="true" level="Non-TeamCity Error" enabled_by_default="true" /> | ||
| <inspection_tool class="JSReferencingArgumentsOutsideOfFunction" enabled="true" level="Non-TeamCity Error" enabled_by_default="true" /> | ||
| <inspection_tool class="Java8MapForEach" enabled="true" level="ERROR" enabled_by_default="true" /> | ||
| <inspection_tool class="JavadocReference" enabled="true" level="ERROR" enabled_by_default="true"> |
There was a problem hiding this comment.
Reverted this change.
There was a problem hiding this comment.
Reverted this change.
| <constraint name="__context__" target="true" within="" contains="" /> | ||
| <constraint name="x" within="" contains="" /> | ||
| <constraint name="y" nameOfExprType="IndexedInts" exprTypeWithinHierarchy="true" within="" contains="" /> | ||
| <constraint name="y" nameOfExprType="IndexedInts" expressionTypes="IndexedInts" exprTypeWithinHierarchy="true" within="" contains="" /> |
There was a problem hiding this comment.
When using different versions of IntelliJ IDEA, it makes some seemingly arbitrary changes to the config by itself. As long as it doesn't break the TeamCity build (and if the PR author uses the latest version of IntelliJ), it should be fine.
There was a problem hiding this comment.
@leventov, thanks for the info. I've reverted the changes auto made by the IDE.
|
|
||
| segmentFileMap.computeIfAbsent(segment, k -> { | ||
| try { | ||
| return segmentLoader.getSegmentFiles(segment); |
There was a problem hiding this comment.
segmentLoader.getSegmentFiles(segment) throws SegmentLoadingException, which needs to be caught and re thrown as RuntimeException. This makes the outer try catch redundant (compilation error) as the SegmentLoadingException has already been caught.
| } | ||
| catch (SegmentLoadingException e) { | ||
| throw new RuntimeException(e); | ||
| } |
There was a problem hiding this comment.
Outer try/catch block removed.
| uniqueMetrics.put(metric, index++); | ||
| uniqueMetrics.computeIfAbsent(metric, k -> { | ||
| return index[0]++; | ||
| } |
There was a problem hiding this comment.
Using index variable as int as is, the compiler complains "Variables used in lambda should be final or effectively final". The fix is to use an integer array with one element. Let me know if this is right.
|
@gianm , @leventov This is the line. Why should ConcurrentMap changed to ConcurrentHashMap type here ? |
This check was added in #6898, I think maybe this comment is relevant to what it is trying to enforce. |
…sent() is called should be assigned into variables of ConcurrentHashMap type, not ConcurrentMap
|
Thanks @clintropolis this helps. |
|
@clintropolis the reference place with the explanation is now here: https://github.com/code-review-checklists/java-concurrency#chm-type |
|
@leventov Please review. |
|
The PR doesn't require my review because it has one approval from a committer and it's not tagged |
|
Thanks @leventov. |
Fix #7316.
if(!map.containsKey(some_key)) {
key.put(some_key, some_value);
}
The above map usage pattern has been replaced with map.putIfAbsent()
A new search configuration got updated in .idea/inspectionProfiles/Druid.xml.