This is not surprising to me at all.
Firstly, you have primitives against the indirectness of the need to pursue links, comparisons between two primitives will be faster, etc.
Secondly, a primitive array will play very well with the processor cache. A non-primitive array is not necessary because there is no guarantee that the referenced objects are adjacent in memory (unlikely), and, in addition, the abstract objects are larger, which means that fewer of them can go into the cache at any time.
See in both cases, the values ββin the arrays will correspond to the cache, but the problem with Integer[] is that you still have to leave the cache and click on the memory bus to chase links and find them in main memory; these links can point all over the heap. This will cause a bad processor to just wait and wait, as cache misses are now much more likely.
That is, you have such an array of primitives as this
_ _ _ _ _ |5| |7| |2| |1| ... |4|
and they all sit next to each other in memory. When a single value is pulled into the cache from memory, neighbors are also pulled into the cache. Quicksort and mergesort work on adjacent sections of the array, so they greatly benefit from the processor cache, which is good here (this is link locality )
But when you have an Integer array like this
_ _ |--->|7| ______> |1| _ | _ | _ | | |_| | | ... |_| | | _ | _ |_____ |________>|4| |___>|5| | _ |__>|2|
Link storage locations are continuous in memory, so they play well with the cache. The problem is * indirectness, the ability to relay Integer objects fragmented in memory, and the fact that fewer of them will go into the cache. This is an additional indirect relation, fragmentation and size problem - this is something that will not play well with the cache.
Again, for something like quicksort or mergesort that plays on adjacent segments of an array, this is huge, huge, huge and almost certainly explains most of the performance difference.
Am I starting it incorrectly?
Yes, please use System.nanoTime the next time you need to run a test. System.currentTimeMillis has a terrible resolution and is not suitable for benchmarking.