The string pool does not prevent garbage collection if no strong references to the interned string exist. A literal is eligible for garbage collection if the containing class is eligible for garbage collection (assuming that no other strong references exist). (See also this thread.)
Static initializers are executed when the class gets initialized, not when it gets loaded. A class can be initialized by accessing a field (that is not a compile time constant), invoking a method, calling Class#forName, etc..., for a more complete list see JLS 12.4.
I did not post the whole code as it would have forced me to rewrite code (not something I wanted to do at ~4am :D); but the previous code should contain sufficient information to add the missing parts.
The following code requires Java9 and compiles; the same is possible with Java8 (with the corresponding classes of sun.* instead of jdk.internal.* - simply replace #getTagAt with a try-catch block, obtain the unsafe instance via reflection and change the regex to match the verbose:class output).
The JVM loads quite a few classes on startup - as the strings are already in the pool at least one of them has to contain code that contains the literal and has to be executed prior the main method is invoked.
A short scan of the loaded classes for a Java9 version (obtained by using -verbose:class) shows, that two of the loaded classes contain the literals:
java.lang.VersionProps - contains "java", reason for "java" being interned (static field initializer contains the literal, class is initialized in init phase 1)
sun.launcher.LauncherHelper - contains "java" and "main", reason for "main" being interned (method for the main-method validation contains the literal)
The used code to scan the classes for the literals:
The objective is to delete (=clear) a range (=sublist) from a list -> the initial approach should be to use list.subList(fromIndex, toIndex).clear().
We expect O(n) performance (as the basic approach would be to shift elements from toIndex to fromIndex and delete the excessive trailing elements, starting with the last one) - a peek into java.util.AbstractList shows that #clear is implemented aswhereas ArrayList and ArrayList$SubList override #removeRange to use an approach similar to the one described above.
I had an eager get method at first - the disadvantage of this is that it requires at least 12+16+4*N bytes per tuple (N := number of elements in a tuple). The lazy tuple requires only 12+4+4=24 bytes (and should be easier for the JIT to inline). (Memory consumption based on 64bit w/ compressed oops.) The huge disadvantage of the lazy tuple implementation is the extremely bad cache locality -> if the tuple has to be accessed often, one can use source.get(index).toArray() (or new ArrayList<>(...) etc.) to get the same performance as with an eager tuple implementation (besides the possible intermediate garbage tuple). As long as the performance details are explicitly documented I would prefer the lazy version over an eager version.
Note: The implementation above is a very simplified implementation, i.e. #indexOf and #contains can be significantly optimized; bulk operations on a tuple can replace modulo operation and offsets array access with one multiplication and one subtraction; etc...
You have to use the result of the ternary operator (assign it to a variable/return it/invoke a method with it/etc.).
(JLS §15.25: "In fact, by the grammar of expression statements (§14.8), it is not permitted for a conditional expression to appear in any context where an invocation of a void method could appear.")
Besides that I would allign the code a bit different:
(You are missing parentheses around fizz * buzz in your last version.)
I would omit the #nullFirst/null-check as Comparable#compareTo specifies to throw a NullPointerException for null arguments.
The alternative compareTo method will fail iff either value is NaN; using the #compare method of the wrapper type avoids possible bugs introduced by forgetting over-/underflow or NaN. In my opinion the comparator based approach should be compared to
I agree that the comparator approach may be better readable iff there is more than one value to compare as each value has to be specified only once instead of twice (but would still use Wrapper#compare/manually written comparison if performance is relevant).