70

I noticed something strange in the implementation of HashMap.clear(). This is how it looked in OpenJDK 7u40:

public void clear() {
    modCount++;
    Arrays.fill(table, null);
    size = 0;
}

And this is how it looks as of OpenJDK 8u40:

public void clear() {
    Node<K,V>[] tab;
    modCount++;
    if ((tab = table) != null && size > 0) {
        size = 0;
        for (int i = 0; i < tab.length; ++i)
            tab[i] = null;
    }
}

I understand that now the table can be null for empty an map, thus the additional check and caching in a local variable is required. But why was Arrays.fill() replaced with a for-loop?

It seems that the change was introduced in this commit. Unfortunately I found no explanation for why a plain for loop might be better than Arrays.fill(). Is it faster? Or safer?

dimo414
  • 47,227
  • 18
  • 148
  • 244
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
  • 3
    Safer surely not, but it _might_ be faster when the `fill` call is not inlined and `tab` is short. On HotSpot both the loop and the explicit `fill` call will result in a fast compiler intrinsic (in a happy-day scenario). – Marko Topolnik Sep 21 '15 at 11:16
  • 14
    I guess that this is to avoid the class `java.util.Arrays` getting loading as a side effect of this method. For application code, this is usually not a concern. – Holger Sep 21 '15 at 11:19
  • 5
    Interesting. My hunch is that this is a mistake. The review thread for this changeset is [here](http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-August/020131.html) and it references an [earlier thread](http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-July/018685.html) that is [continued here](http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-August/019853.html). The initial message in that earlier thread points to a prototype of HashMap.java in Doug Lea's CVS repository. I don't know where this came from. It doesn't seem to match anything in the OpenJDK history. – Stuart Marks Sep 21 '15 at 20:24
  • 7
    ... In any case, it might have been some old snapshot; the for-loop was in the clear() method for many years. The Arrays.fill() call was introduced by [this changeset](http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/2e3cc7f599ca), so it was in the tree only for a few months. Note also that the power-of-two computation based on Integer.highestOneBit() introduced by [this changeset](http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/36f4cf8872f3) also disappeared at the same time, though this was noted but dismissed during the review. Hmmm. – Stuart Marks Sep 21 '15 at 20:35
  • @Holger This makes sense since `Arrays.fill()` is doing the exact same [for loop](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/Arrays.java#Arrays.fill%28java.lang.Object%5B%5D%2Cjava.lang.Object%29) – Bruno Grieder Sep 23 '15 at 15:45
  • 1
    @SotiriosDelimanolis I will vote to close this when your bounty expires. It is purely opinion-based and should be addressed to the author of the code. – user207421 Sep 23 '15 at 21:51
  • 2
    @EJP I probably will too. I just want to see what comes out of Stuart's investigation. – Sotirios Delimanolis Sep 23 '15 at 21:53
  • 6
    @EJP, I disagree. I'm not looking for opinions, just for facts, the question is not about code style. There are three good versions in the comments, all can be verified. Marko's version can be verified by good benchmark executed in interpreted/C1/C2 mode. Holger's version can be verified by investigating class loading order on JVM startup. Stuart's version (which is likely to be correct) can be verified by investigating commit tree and mailing list discussions. After all anybody can ask this directly in core-libs-dev. There are thousands of questions where correct answer is "it's a mistake". – Tagir Valeev Sep 24 '15 at 01:23
  • 1
    Then you're looking for facts in the wrong place. What would SO users know about the reasons for design or coding choices inside Java? – user207421 Oct 01 '15 at 00:07
  • 3
    @EJP, I see no big difference between my question and, for example, [this](http://stackoverflow.com/q/19488357/4856258). You may also say that this is about "reasons for design or coding choices inside Java", but it can be answered by anybody (not only by JDK developers). It was possible that answer to my question is known. I cannot knew in advance. There are also many great questions (like [this](http://stackoverflow.com/q/25443655/4856258)) specially targeted to JDK developers (cannot be answered by anybody else) and JDK developers answer. – Tagir Valeev Oct 01 '15 at 01:06
  • @TagirValeev Did you ask the author of the change? :) – ZhekaKozlov Feb 01 '17 at 14:39

5 Answers5

30

I will try to summarize three moreless reasonable versions which were proposed in comments.

@Holger says:

I guess that this is to avoid the class java.util.Arrays getting loading as a side effect of this method. For application code, this is usually not a concern.

This is the most easy thing to test. Let's compile such program:

public class HashMapTest {
    public static void main(String[] args) {
        new java.util.HashMap();
    }
}

Run it with java -verbose:class HashMapTest. This will print the class loading events as they occur. With JDK 1.8.0_60 I see more than 400 classes loaded:

... 155 lines skipped ...
[Loaded java.util.Set from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.AbstractSet from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.Collections$EmptySet from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.Collections$EmptyList from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.Collections$EmptyMap from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.Collections$UnmodifiableCollection from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.Collections$UnmodifiableList from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.Collections$UnmodifiableRandomAccessList from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded sun.reflect.Reflection from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
**[Loaded java.util.HashMap from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.HashMap$Node from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.Class$3 from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.Class$ReflectionData from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.Class$Atomic from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded sun.reflect.generics.repository.AbstractRepository from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded sun.reflect.generics.repository.GenericDeclRepository from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded sun.reflect.generics.repository.ClassRepository from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.Class$AnnotationData from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded sun.reflect.annotation.AnnotationType from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.util.WeakHashMap from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.ClassValue$ClassValueMap from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.reflect.Modifier from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded sun.reflect.LangReflectAccess from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
[Loaded java.lang.reflect.ReflectAccess from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
**[Loaded java.util.Arrays from C:\Program Files\Java\jre1.8.0_60\lib\rt.jar]
...

As you can see, HashMap is loaded long before application code and Arrays is loaded only 14 classes after HashMap. The HashMap load is triggered by sun.reflect.Reflection initialization as it has HashMap static fields. The Arrays load is likely to be triggered by WeakHashMap load which actually has Arrays.fill in the clear() method. The WeakHashMap load is triggered by java.lang.ClassValue$ClassValueMap which extends WeakHashMap. The ClassValueMap is present in every java.lang.Class instance. So to me seems that without Arrays class the JDK cannot be initialized at all. Also the Arrays static initializer is very short, it only initializes the assertion mechanism. This mechanism is used in many other classes (including, for example, java.lang.Throwable which is loaded very early). No other static initialization steps are performed in java.util.Arrays. Thus @Holger version seems incorrect to me.

Here we also found very interesting thing. The WeakHashMap.clear() still uses Arrays.fill. It's interesting when it appeared there, but unfortunately this goes to prehistoric times (it was already there in the very first public OpenJDK repository).

Next, @MarcoTopolnik says:

Safer surely not, but it might be faster when the fill call is not inlined and tab is short. On HotSpot both the loop and the explicit fill call will result in a fast compiler intrinsic (in a happy-day scenario).

It was actually surprising for me that Arrays.fill is not directly intrinsified (see intrinsic list generated by @apangin). Seems that such loop can be recognized and vectorized by JVM without explicit intrinsic handling. So it's true that extra call can be not inlined in very specific cases (for example if MaxInlineLevel limit is reached). On the other hand it's very rare situation and it's only a single call, it's not a call inside loop, and it's a static, not virtual/interface call, thus the performance improvement could be only marginal and only in some specific scenarios. Not the thing the JVM developers usually care.

Also it should be noted that even C1 'client' compiler (tier 1-3) is capable to inline Arrays.fill called, for example, in WeakHashMap.clear(), as inlining log (-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining) says:

36       3  java.util.WeakHashMap::clear (50 bytes)
     !m        @ 4   java.lang.ref.ReferenceQueue::poll (28 bytes)
                 @ 17   java.lang.ref.ReferenceQueue::reallyPoll (66 bytes)   callee is too large
               @ 28   java.util.Arrays::fill (21 bytes)
     !m        @ 40   java.lang.ref.ReferenceQueue::poll (28 bytes)
                 @ 17   java.lang.ref.ReferenceQueue::reallyPoll (66 bytes)   callee is too large
               @ 1   java.util.AbstractMap::<init> (5 bytes)   inline (hot)
                 @ 1   java.lang.Object::<init> (1 bytes)   inline (hot)
               @ 9   java.lang.ref.ReferenceQueue::<init> (27 bytes)   inline (hot)
                 @ 1   java.lang.Object::<init> (1 bytes)   inline (hot)
                 @ 10   java.lang.ref.ReferenceQueue$Lock::<init> (5 bytes)   unloaded signature classes
               @ 62   java.lang.Float::isNaN (12 bytes)   inline (hot)
               @ 112   java.util.WeakHashMap::newTable (8 bytes)   inline (hot)

Of course, it's also easily inlined by smart and powerful C2 'server' compiler. Thus I see no problems here. Seems that @Marco version is incorrect either.

Finally we have a couple of comments from @StuartMarks (who is JDK developer, thus some official voice):

Interesting. My hunch is that this is a mistake. The review thread for this changeset is here and it references an earlier thread that is continued here. The initial message in that earlier thread points to a prototype of HashMap.java in Doug Lea's CVS repository. I don't know where this came from. It doesn't seem to match anything in the OpenJDK history.

... In any case, it might have been some old snapshot; the for-loop was in the clear() method for many years. The Arrays.fill() call was introduced by this changeset, so it was in the tree only for a few months. Note also that the power-of-two computation based on Integer.highestOneBit() introduced by this changeset also disappeared at the same time, though this was noted but dismissed during the review. Hmmm.

Indeed the HashMap.clear() contained the loop many years, was replaced with Arrays.fill on Apr 10th, 2013 and stayed less one half-a-year until Sept 4th when the discussed commit was introduced. The discussed commit was actually a major rewrite of the HashMap internals to fix JDK-8023463 issue. It was a long story about possibility to poison the HashMap with keys having duplicating hashcodes reducing HashMap search speed to linear making it vulnerable to DoS-attacks. The attempts to solve this were performed in JDK-7 including some randomization of String hashCode. So seems that the HashMap implementation was forked from the earlier commit, developed independently, then merged into the master branch overwriting several changes introduced in-between.

We may support this hypothesis performing a diff. Take the version where Arrays.fill was removed (2013-09-04) and compare it with previous version (2013-07-30). The diff -U0 output has 4341 lines. Now let's diff against the version prior to one when Arrays.fill was added (2013-04-01). Now diff -U0 contains only 2680 lines. Thus the newer version actually more similar to the older than to immediate parent.

Conclusion

So to conclude I would agree with Stuart Marks. There were no concrete reason to remove Arrays.fill, it's just because the in-between change was overwritten by mistake. Using Arrays.fill is perfectly fine both in JDK code and in user applications and used, for example, in WeakHashMap. The Arrays class is loaded anyways pretty early during the JDK initialization, has very simple static initializer and Arrays.fill method can be easily inlined even by client compiler, so no performance drawback should be noted.

Community
  • 1
  • 1
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
  • My comment was only guessing an *intention*, not claiming that it was actually useful given the current implementation. A simpler test consist of just setting a breakpoint in `HashMap.clear()` to catch the very first (JVM internal) invocation and yes, by that time, `java.util.Arrays` has been loaded already. To me, Stuart Marks has already given the answer two days ago. There is actually a tendency to do the opposite, replace manual loops by `Arrays.fill` for good reasons and here, we have just clashing updates. Btw. `Arrays.fill` might become intrinsic just like `Arrays.copyOf` already is… – Holger Sep 24 '15 at 11:35
  • You said - *The Arrays load is likely to be triggered by WeakHashMap load which actually has Arrays.fill in the clear() method.*. A class is only loaded if that class is needed when loading the current class. So, `Arrays` will only be loaded while loading `WeakHashMap` if `WeakHashMap` has a static intialization of `Arrays`, just having `Arrays.fill()` in `clear` method will not trigger the loading of `Arrays`. I think some other class is triggering the load of `Arrays`. – hagrawal7777 Sep 24 '15 at 14:24
  • I don't agree with the conclusion; it seems unlikely that it was a mistake in branch merging. To me, the developer simply *thought* that writing the loop directly instead of calling `Arrays.fill` *could* be faster (due to the reasons related to class loading and method inlining noted elsewhere), and since the loop is quite simple & small, proceeded to do it. – Rogério Sep 24 '15 at 17:39
  • @hagrawal: you are right in that a static reference does not automatically trigger class loading/initialization. However, it’s not that hard to find the actual trigger by adding an initializer to the `Arrays` class which records the call stack trace. In the jdk, I tested, it’s the constructor `String(char[])` which calls `Arrays.copy`. It was called by `sun.nio.cs.FastCharsetProvider`, but I guess, there are plenty of other places that initialized it, if that caller wasn’t the first… – Holger Sep 25 '15 at 13:13
  • @Holger Ok. To me it looks (*I think you also mean it*) that `Arrays` isn't loaded because of `sun.nio.cs.FastCharsetProvider` because `Arrays` get loaded much before it. As per my knowledge, a class will be loaded soon after the class which is referencing it, so going by that logic, `Arrays` should have been loaded because of `sun.reflect.misc.ReflectUtil` or `java.util.concurrent.atomic.AtomicReferenceFieldUpdater` because these classes are loaded just before `Arrays` – hagrawal7777 Sep 25 '15 at 15:38
  • @hagrawal: I’m not sure whether the `verbose` option reports loading (as it says) or initialization. The loading might be eagerly for certain system classes. However, it might be the case that we are talking about different versions now. In one JRE, I tested, there was indeed the chain `AtomicReferenceFieldUpdater` → `ReflectUtil` → `String(char[],int,int)` → `Arrays.copyOfRange(…)`, then I realized that this was a pretty old JRE, and I tested with `1.8.0_60` where `FastCharsetProvider` was the trigger as said in my previous comment. However, in both cases, a `String` constructor was involved… – Holger Sep 25 '15 at 15:46
  • @Holger How you are getting `String(char[],int,int)`, I looked at http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b27/sun/reflect/misc/ReflectUtil.java and I don't see any reference of such code .. On a side note, what was your approach for recording call stack ?? – hagrawal7777 Sep 25 '15 at 15:54
3

Because it's much faster!

I ran some thorough benchmarking tests on cut down versions of the two methods:

void jdk7clear() {
    Arrays.fill(table, null);
}

void jdk8clear() {
    Object[] tab;
    if ((tab = table) != null) {
        for (int i = 0; i < tab.length; ++i)
            tab[i] = null;
    }
}

operating on arrays of various sizes containing random values. Here are the (typical) results:

Map size |  JDK 7 (sd)|  JDK 8 (sd)| JDK 8 vs 7
       16|   2267 (36)|   1521 (22)| 67%
       64|   3781 (63)|   1434 ( 8)| 38%
      256|   3092 (72)|   1620 (24)| 52%
     1024|   4009 (38)|   2182 (19)| 54%
     4096|   8622 (11)|   4732 (26)| 55%
    16384|  27478 ( 7)|  12186 ( 8)| 44%
    65536| 104587 ( 9)|  46158 ( 6)| 44%
   262144| 445302 ( 7)| 183970 ( 8)| 41%

And here are the results when operating over an array filled with nulls (so garbage collection issues are eradicated):

Map size |  JDK 7 (sd)|  JDK 8 (sd)| JDK 8 vs 7
       16|     75 (15)|     65 (10)|  87%
       64|    116 (34)|     90 (15)|  78%
      256|    246 (36)|    191 (20)|  78%
     1024|    751 (40)|    562 (20)|  75%
     4096|   2857 (44)|   2105 (21)|  74%
    16384|  13086 (51)|   8837 (19)|  68%
    65536|  52940 (53)|  36080 (16)|  68%
   262144| 225727 (48)| 155981 (12)|  69%

The numbers are in nanoseconds, (sd) is 1 standard deviation expressed as a percentage of the result (fyi, a "normally distributed" population has an SD of 68), vs is the JDK 8 timing relative to JDK 7.

It is interesting that not only is it significantly faster, but the deviation is also slightly narrower, which means that the JDK 8 implementation gives slightly more consistent performance.

The tests were run on jdk 1.8.0_45 over a large (millions) number of times on arrays populated with random Integer objects. To remove out-lying numbers, on each set of results the fastest and slowest 3% of timings were discarded. Garbage collection was requested and the thread yielded and slept just prior to running each invocation of the method. JVM warm up was done on the first 20% of work and those results were discarded.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • 2
    Could you please share the details on benchmarking framework you are using. When did you make explicit garbage collection? Did you run separate tests in separate JVMs? The output does not look like JMH. Also if you have sd 16% (quite high sd for this case, probably testing methodology problems?), you cannot say that 3% faster result is statistically significant. It can be random fluctuation. – Tagir Valeev Sep 30 '15 at 01:41
  • @TagirValeev I wrote my own test. Allowed for warm up. You can say 3% is statistically significant because of the volume of tests (millions) and it is so consistent even run. I did not allow for GC (that could explain big SD). I'll run it again controlling GC. Note that SD is *that* big - a standard normal distribution has SD = 68%, but I'm with you - I would expect the same code to have a quite tight variance. – Bohemian Sep 30 '15 at 05:45
  • @Tagir wow. With a call to gc() + yield() + sleep(1) before each invocation, the results are shocking. – Bohemian Sep 30 '15 at 12:43
  • 4
    Could you please share the whole benchmarking code? When phenomena like [neutrinos travelling faster than light](https://en.wikipedia.org/wiki/Faster-than-light_neutrino_anomaly) are discovered, it's usually a measurement error rather than a revolution in Physics. – Tagir Valeev Sep 30 '15 at 14:45
  • @TagirValeev OK. Let me clean it up and find a place to park it and link. – Bohemian Sep 30 '15 at 15:19
  • 3
    I did my own benchmarking last night, but I could only see tiny differences in performance betwen the two `clear()` method versions (running on JDK 1.8.0_45 32-bit). So, this answer will need to show complete source code for the benchmark, otherwise it just can't be trusted. – Rogério Oct 02 '15 at 16:36
  • 2
    If answers could be closed on SO, I would vote to close this answer. – ZhekaKozlov Feb 01 '17 at 14:41
1

There is no actual difference in the functionality between the 2 version's loop. Arrays.fill does the exact same thing.

So the choice to use it or not may not necessarily be considered a mistake. It is left up to the developer to decide when it comes to this kind of micromanagement.

There are 2 separate concerns for each approach:

  • using the Arrays.fill makes the code less verbose and more readable.
  • looping directly in the HashMap code (like version 8) peformance wise is actually a better option. While the overhead that inserting the Arrays class is negligible it may become less so when it comes to something as widespread as HashMap where every bit of performance enhancement has a large effect(imagine the tiniest footprint reduce of a HashMap in fullblown webapp). Take into consideration the fact that the Arrays class was used only for this one loop. The change is small enough that it doesn't make the clear method less readable.

The precise reason can't be found out without asking the developer who actually did this, however i suspect it's either a mistake or a small enhancement. better option.

My opinion is it can be considered an enhancement, even if only by accident.

Laurentiu L.
  • 6,566
  • 1
  • 34
  • 60
  • Your benchmark is interesting, but may be the result of using a newer JVM. Try running copies of the source snippets in java 8 JVM. – Bohemian Sep 23 '15 at 16:34
  • 7
    Your benchmark does not look right (where is the warm up? how many runs did you do etc.) - and 11 or 18 ms is close to the resolution of currentTimeMillis on some systems. So I would not trust those results very much... – assylias Sep 23 '15 at 16:45
  • Indeed, it is a bit sketchy and may not necessarily pinpoint to that exact change. Will update with the exact code snippets. As for the runs it was about 20 with a +-1 ms variation. – Laurentiu L. Sep 23 '15 at 16:53
1

I'm going to shoot in the dark here...

My guess is that it might have been changed in order to prepare the ground for Specialization (aka generics over primitive types). Maybe (and I insist on maybe), this change is meant to make transition to Java 10 easier, in the event of specialization being part of the JDK.

If you look at the State of the Specialization document, Language restrictions section, it says the following:

Because any type variables can take on value as well as reference types, the type checking rules involving such type variables (henceforth, "avars"). For example, for an avar T:

  • Cannot convert null to a variable whose type is T
  • Cannot compare T to null
  • Cannot convert T to Object
  • Cannot convert T[] to Object[]
  • ...

(Emphasis is mine).

And ahead in the Specializer transformations section, it says:

When specializing an any-generic class, the specializer is going to perform a number of transformations, most localized, but some requiring a global view of a class or method, including:

  • ...
  • Type variable substitution and name mangling is performed on the signatures of all methods
  • ...

Later on, near the end of the document, in the Further investigation section, it says:

While our experiments have proven that specialization in this manner is practical, much more investigation is needed. Specifically, we need to perform a number of targeted experiments aimed at any-fying core JDK libraries, specifically Collections and Streams.


Now, regarding the change...

If the Arrays.fill(Object[] array, Object value) method is going to be specialized, then its signature should change to Arrays.fill(T[] array, T value). However this case is specifically listed in the (already mentioned) Language restrictions section (it would violate the emphasized items). So maybe someone decided that it would be better to not use it from the HashMap.clear() method, especially if value is null.

Community
  • 1
  • 1
fps
  • 33,623
  • 8
  • 55
  • 110
0

For me, the reason is a likely performance inprovement, at a negligible cost in terms of code clarity.

Note that the implementation of the fill method is trivial, a simple for-loop setting each array element to null. So, replacing a call to it with the actual implementation does not cause any significant degradation in the clarity/conciseness of the caller method.

The potential performance benefits are not so insignificant, if you consider everything that is involved:

  1. There will be no need for the JVM to resolve the Arrays class, plus loading and initializing it if needed. This is a non-trivial process where the JVM performs several steps. Firstly, it checks the class loader to see if the class is already loaded, and this happens every time a method is called; there are optimizations involved here, of course, but it still takes some effort. If the class is not loaded, the JVM will need to go through the expensive process of loading it, verifying the bytecode, resolving other necessary dependencies, and finally performing static initialization of the class (which can be arbitrarily expensive). Given that HashMap is such a core class, and that Arrays is such a huge class (3600+ lines), avoiding these costs may add up to noticeable savings.

  2. Since there is no Arrays.fill(...) method call, the JVM won't have to decide whether/when to inline the method into the caller's body. Since HashMap#clear() tends to get called a lot, the JVM will eventually perform the inlining, which requires JIT recompilation of the clear method. With no method calls, clear will always run at top-speed (once initially JITed).

Another benefit of no longer calling methods in Arrays is that it simplifies the dependency graph inside the java.util package, since one dependency is removed.

Rogério
  • 16,171
  • 2
  • 50
  • 63