Source code
package com.google.common.collect;
import com.google.common.annotations.GwtCompatible;
import com.google.common.primitives.Booleans;
import com.google.errorprone.annotations.InlineMe;
import java.util.Comparator;
import org.jspecify.annotations.Nullable;
/**
* A utility for performing a chained comparison statement. <b>Note:</b> Java 8+ users should
* generally prefer the methods in {@link Comparator}; see <a href="#java8">below</a>.
*
* <p>Example usage of {@code ComparisonChain}:
*
* {@snippet :
* public int compareTo(Foo that) {
* return ComparisonChain.start()
* .compare(this.aString, that.aString)
* .compare(this.anInt, that.anInt)
* .compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
* .result();
* }
* }
*
* <p>The value of this expression will have the same sign as the <i>first nonzero</i> comparison
* result in the chain, or will be zero if every comparison result was zero.
*
* <p><b>Note:</b> {@code ComparisonChain} instances are <b>immutable</b>. For this utility to work
* correctly, calls must be chained as illustrated above.
*
* <p>Performance note: Even though the {@code ComparisonChain} caller always invokes its {@code
* compare} methods unconditionally, the {@code ComparisonChain} implementation stops calling its
* inputs' {@link Comparable#compareTo compareTo} and {@link Comparator#compare compare} methods as
* soon as one of them returns a nonzero result. This optimization is typically important only in
* the presence of expensive {@code compareTo} and {@code compare} implementations.
*
* <p>See the Guava User Guide article on <a href=
* "https://github.com/google/guava/wiki/CommonObjectUtilitiesExplained#comparecompareto">{@code
* ComparisonChain}</a>.
*
* <h4 id="java8">Java 8+ equivalents</h4>
*
* If you are using Java version 8 or greater, you should generally use the static methods in {@link
* Comparator} instead of {@code ComparisonChain}. The example above can be implemented like this:
*
* {@snippet :
* import static java.util.Comparator.comparing;
* import static java.util.Comparator.nullsLast;
* import static java.util.Comparator.naturalOrder;
*
* ...
* private static final Comparator<Foo> COMPARATOR =
* comparing((Foo foo) -> foo.aString)
* .thenComparing(foo -> foo.anInt)
* .thenComparing(foo -> foo.anEnum, nullsLast(naturalOrder()));
*
* @Override
* public int compareTo(Foo that) {
* return COMPARATOR.compare(this, that);
* }
* }
*
* <p>With method references it is more succinct: {@code comparing(Foo::aString)} for example.
*
* <p>Using {@link Comparator} avoids certain types of bugs, for example when you meant to write
* {@code .compare(a.foo, b.foo)} but you actually wrote {@code .compare(a.foo, a.foo)} or {@code
* .compare(a.foo, b.bar)}. {@code ComparisonChain} also has a potential performance problem that
* {@code Comparator} doesn't: it evaluates all the parameters of all the {@code .compare} calls,
* even when the result of the comparison is already known from previous {@code .compare} calls.
* That can be expensive.
*
* @author Mark Davis
* @author Kevin Bourrillion
* @since 2.0
*/
@GwtCompatible
public abstract class ComparisonChain {
private ComparisonChain() {}
/** Begins a new chained comparison statement. See example in the class documentation. */
public static ComparisonChain start() {
return ACTIVE;
}
private static final ComparisonChain ACTIVE =
new ComparisonChain() {
@SuppressWarnings("unchecked") // unsafe; see discussion on supertype
@Override
public ComparisonChain compare(Comparable<?> left, Comparable<?> right) {
return classify(((Comparable<Object>) left).compareTo(right));
}
@Override
public <T extends @Nullable Object> ComparisonChain compare(
@ParametricNullness T left, @ParametricNullness T right, Comparator<T> comparator) {
return classify(comparator.compare(left, right));
}
@Override
public ComparisonChain compare(int left, int right) {
return classify(Integer.compare(left, right));
}
@Override
public ComparisonChain compare(long left, long right) {
return classify(Long.compare(left, right));
}
@Override
public ComparisonChain compare(float left, float right) {
return classify(Float.compare(left, right));
}
@Override
public ComparisonChain compare(double left, double right) {
return classify(Double.compare(left, right));
}
@Override
public ComparisonChain compareTrueFirst(boolean left, boolean right) {
return classify(Boolean.compare(right, left)); // reversed
}
@Override
public ComparisonChain compareFalseFirst(boolean left, boolean right) {
return classify(Boolean.compare(left, right));
}
ComparisonChain classify(int result) {
return (result < 0x0) ? LESS : (result > 0x0) ? GREATER : ACTIVE;
}
@Override
public int result() {
return 0x0;
}
};
private static final ComparisonChain LESS = new InactiveComparisonChain(-0x1);
private static final ComparisonChain GREATER = new InactiveComparisonChain(0x1);
private static final class InactiveComparisonChain extends ComparisonChain {
final int result;
InactiveComparisonChain(int result) {
this.result = result;
}
@Override
public ComparisonChain compare(Comparable<?> left, Comparable<?> right) {
return this;
}
@Override
public <T extends @Nullable Object> ComparisonChain compare(
@ParametricNullness T left, @ParametricNullness T right, Comparator<T> comparator) {
return this;
}
@Override
public ComparisonChain compare(int left, int right) {
return this;
}
@Override
public ComparisonChain compare(long left, long right) {
return this;
}
@Override
public ComparisonChain compare(float left, float right) {
return this;
}
@Override
public ComparisonChain compare(double left, double right) {
return this;
}
@Override
public ComparisonChain compareTrueFirst(boolean left, boolean right) {
return this;
}
@Override
public ComparisonChain compareFalseFirst(boolean left, boolean right) {
return this;
}
@Override
public int result() {
return result;
}
}
/**
* Compares two comparable objects as specified by {@link Comparable#compareTo}, <i>if</i> the
* result of this comparison chain has not already been determined.
*
* <p>This method is declared to accept any 2 {@code Comparable} objects, even if they are not <a
* href="https://docs.oracle.com/javase/tutorial/collections/interfaces/order.html">mutually
* comparable</a>. If you pass objects that are not mutually comparable, this method may throw an
* exception. (The reason for this decision is lost to time, but the reason <i>might</i> be that
* we wanted to support legacy classes that implement the raw type {@code Comparable} (instead of
* implementing {@code Comparable<Foo>}) without producing warnings. If so, we would prefer today
* to produce warnings in that case, and we may change this method to do so in the future. Support
* for raw {@code Comparable} types in Guava in general is tracked as <a
* href="https://github.com/google/guava/issues/989">#989</a>.)
*
* @throws ClassCastException if the parameters are not mutually comparable
*/
public abstract ComparisonChain compare(Comparable<?> left, Comparable<?> right);
/**
* Compares two objects using a comparator, <i>if</i> the result of this comparison chain has not
* already been determined.
*/
public abstract <T extends @Nullable Object> ComparisonChain compare(
@ParametricNullness T left, @ParametricNullness T right, Comparator<T> comparator);
/**
* Compares two {@code int} values as specified by {@link Integer#compare}, <i>if</i> the result
* of this comparison chain has not already been determined.
*/
public abstract ComparisonChain compare(int left, int right);
/**
* Compares two {@code long} values as specified by {@link Long#compare}, <i>if</i> the result of
* this comparison chain has not already been determined.
*/
public abstract ComparisonChain compare(long left, long right);
/**
* Compares two {@code float} values as specified by {@link Float#compare}, <i>if</i> the result
* of this comparison chain has not already been determined.
*/
public abstract ComparisonChain compare(float left, float right);
/**
* Compares two {@code double} values as specified by {@link Double#compare}, <i>if</i> the result
* of this comparison chain has not already been determined.
*/
public abstract ComparisonChain compare(double left, double right);
/**
* Discouraged synonym for {@link #compareFalseFirst}.
*
* @deprecated Use {@link #compareFalseFirst}; or, if the parameters passed are being either
* negated or reversed, undo the negation or reversal and use {@link #compareTrueFirst}.
* @since 19.0
*/
@InlineMe(replacement = "this.compareFalseFirst(left, right)")
@Deprecated
public final ComparisonChain compare(Boolean left, Boolean right) {
return compareFalseFirst(left, right);
}
/**
* Compares two {@code boolean} values, considering {@code true} to be less than {@code false},
* <i>if</i> the result of this comparison chain has not already been determined.
*
* <p>Java 8+ users: you can get the equivalent from {@link Booleans#trueFirst()}. For example:
*
* <pre>
* Comparator.comparing(Foo::isBar, {@link Booleans#trueFirst()})
* </pre>
*
* @since 12.0
*/
public abstract ComparisonChain compareTrueFirst(boolean left, boolean right);
/**
* Compares two {@code boolean} values, considering {@code false} to be less than {@code true},
* <i>if</i> the result of this comparison chain has not already been determined.
*
* <p>Java 8+ users: you can get the equivalent from {@link Booleans#falseFirst()}. For example:
*
* <pre>
* Comparator.comparing(Foo::isBar, {@link Booleans#falseFirst()})
* </pre>
*
* @since 12.0 (present as {@code compare} since 2.0)
*/
public abstract ComparisonChain compareFalseFirst(boolean left, boolean right);
/**
* Ends this comparison chain and returns its result: a value having the same sign as the first
* nonzero comparison result in the chain, or zero if every result was zero.
*/
public abstract int result();
}
Run 1
The code is highly readable overall. It has a clear, logical structure with well-organized abstract methods and private implementations. Nesting is minimal; the use of anonymous and static inner classes keeps the hierarchy flat. The intent is clearly documented with extensive Javadocs and practical examples, aiding comprehension. Code length is somewhat long due to documentation, but this is appropriate for a public utility API. Each line performs a single clear action, maintaining good action granularity. Reading flow is natural: the class declaration, constants, abstract methods, and inner classes follow a logical progression. Minor deductions arise from the repetitive overriding of compare methods in InactiveComparisonChain, which slightly reduces conciseness, and the use of hex literals like 0x0 could be clearer as decimal constants. Overall, the code is very well-structured and maintainable.
Run 2
The code is well-structured into clearly named classes and methods, with good separation between the active and inactive chain states. Nesting is minimal, with only a few single-level anonymous/static inner classes. Intent is explicit through descriptive method names (compareTrueFirst, result) and thorough Javadoc. The logic is concise for its purpose, though method overloading for each primitive type adds length. Each action is on its own line, and reading flow is natural, with public API declarations followed by private implementations. The main readability drawbacks are the numerous overloaded methods and some abstract/implementation repetition, but overall the code is highly maintainable and clear.
Run 3
The code is well-structured with a clear logical organization: an abstract base class defines the API, while anonymous/inner subclasses implement active and inactive chain states. The documentation is extensive but can be skipped during code reading. Nesting is minimal, and action granularity is good — each compare method performs one action. The comparison logic is clear: ACTIVE performs real comparisons and returns LESS/GREATER/ACTIVE, while inactive states short-circuit. The primary readability issues are the length driven by Javadoc, some duplicated method signatures, and the slightly obscure use of 0x0/-0x1/0x1 literals rather than plain 0/-1/1. The 'unsafe' cast also requires careful reading.
Run 1
Code Structure: The active/inactive state design cleanly separates comparison execution from preserving an established result, with consistently grouped overloads. Nesting: Methods are mostly flat; the anonymous implementation and nested ternary in classify add modest complexity. Clarity of intent: Descriptive names, explicit boolean ordering, and comprehensive documentation make behavior clear. Hexadecimal constants for ordinary comparison results and an unchecked cast slightly obscure otherwise straightforward logic. Code length: Individual methods are concise, but repeated overloads and extensive documentation make the file lengthy, largely justified by the API. Action granularity: Statements generally perform one action per line, with compact delegation and return expressions. Reading flow: Consistent formatting and parallel implementations support predictable reading, though placing public abstract declarations after internal implementations requires some navigation.
Run 2
Code Structure: Clear separation between active comparisons, inactive states, and the public API; the anonymous active implementation adds some complexity. Nesting: Generally shallow, with short methods; the nested ternary in classify slightly reduces readability. Clarity of intent: Descriptive names and comprehensive documentation explain chaining, ordering, and short-circuit behavior well. Hexadecimal representations of zero and one are unnecessarily unusual. Code length: Numerous overloads and repeated inactive implementations make the class lengthy, although most repetition supports the API; documentation is helpful but occasionally verbose. Action granularity: Methods generally perform one clear operation per statement, with simple returns and assignments. Reading flow: Consistent formatting and parallel method ordering make scanning natural, but placing public API declarations after the implementations requires some navigation.
Run 3
Code Structure: Clearly separates active comparison behavior, inactive states, and the public API, though the anonymous implementation adds some complexity. Nesting: Methods are shallow; the nested ternary in classify is the main readability drawback. Clarity of intent: Descriptive names and thorough documentation explain chaining, ordering, and short-circuit behavior well. Hexadecimal literals for ordinary comparison results are unnecessarily distracting. Code length: Individual methods are concise, but repeated overloads and extensive documentation make the class lengthy; most repetition supports the API. Action granularity: Statements generally perform one action per line, with compact comparison-and-classification expressions remaining understandable. Reading flow: Consistent formatting and parallel method ordering support scanning, although placing implementations before public method declarations requires some navigation.