Tips
"Effective Java, third Edition" an English version has been published, the second edition of this book presumably many people have read, known as one of the four major Java books, but the second edition of 2009 published, to now nearly 8 years, but with Java 6, 7, 8, and even 9 of the release, the Java language has undergone profound changes.
In the first time here translated into Chinese version. For everyone to learn to share.
? 14. Consider implementing the comparable interface
Unlike other methods discussed in this chapter,compareTomethods are not declared in aObjectclass. Instead, it is theComparableonly method in the interface. It isObjectequalssimilar in nature to the method of the class, except that it allows a comparison of orders outside of a simple equality comparison, which is generic. By implementingComparablean interface, a class indicates that its instance has a natural order (? Natural ordering).Comparableordering an array of objects that implement an interface is straightforward, as follows:
Arrays.sort(a);
It is easy to find, calculate extreme values, and maintainComparableautomatic sorting of object collections. For example, in the following code, theStringclass implements theComparableinterface, removes the command-line arguments, enters a repeating string, and sorts it in alphabetical order:
public class WordList {
????public static void main(String[] args) {
????????Set<String> s = new TreeSet<>();
????????Collections.addAll(s, args);
????????System.out.println(s);
????}
}
By implementing anComparableinterface, you can have your class interoperate with all common algorithms and collection implementations that rely on this interface. It takes a small amount of effort to get great energy. Almost all value classes in the Java Platform Class Library and all enumeration types (entry 34) implementComparableinterfaces. If you are writing a value class that has a clear natural order (such as alphabetical order, numeric order, or chronological order), you should implement theComparableinterface:
public interface Comparable<T> {
????int compareTo(T t);
}
compareToCommon conventions for methods areequalssimilar to:
Compares this object to the specified object by sort. The return value may be a negative integer, 0 or a positive integer, because this object corresponds to less than, equal to, or greater than the specified object. If the type of the specified object cannot be compared with this object, an exception is thrownClassCastException.
In the following description, the symbol SGN (expression) represents the Signum function in mathematics, which corresponds to return 1, 0, and 1, depending on the value of the expression as negative, 0, positive.
- The implementation class must ensure that allxand allyare satisfiedsgn(x.compareTo(y)) == -sgn(y. compareTo(x)). (This means that an exception must be thrown when and only if any.compareTo(x)exception is thrownx.compareTo(y).) )
- The implementation class must also ensure that the relationship is transitive:(x. compareTo(y) > 0 && y.compareTo(z) > 0)meansx.compareTo(z) > 0.
-
Finally, for all z, the implementation class must ensure that it[x.compareTo(y) == 0meanssgn(x.compareTo(z)) == sgn(y.compareTo(z)).
-
Highly recommendedx.compareTo(y) == 0) == (x.equals(y)), but not required. In general, any class that implements anComparableinterface that violates this condition should clearly state the fact. The recommended language is "NOTE: This class has a natural order,equalsinconsistent with".
As with theequalsmethod, do not be deterred by the mathematical characteristics of the above conventions. This Convention is not as complex as it seems. Unlikeequalsmethods,equalsa method imposes a global equivalence relationship on all objectscompareTowithout having to cross different types of objects: AncompareToexception is allowed when a different type of object is encounteredClassCastException. Usually, that's exactly what it does. The conventions do allow comparisons between different types, which are typically defined in the interfaces implemented by the objects being compared.
Just as ahashCodeclass that violates the Convention may break other classes that depend on hashing, acompareToclass that violates the contract may break other classes that depend on the comparison. Classes that rely on comparisons, including sorted collectionsTreeSetandTreeMapclasses, and utility classes that contain search and sorting algorithmsCollectionsArrays.
Let's takecompareToa look at the agreed rules. The first rule is that if you invert the comparison direction between two object references, the expected thing will happen: if the first object is smaller than the second object, then the second object must be greater than the first one; If the first object equals the second one, then the second object must be equal to the first one; If the first object is greater than the second one, then the second must be less than the first one. The second convention says that if an object is larger than the second object, and the second object is larger than the third object, the first object must be greater than the third object. The last rule is that all objects that compare equal must have the same result compared to any other object.
One result of these three provisions is that thecompareToequality tests implemented by the method must comply with theequalssame limitations imposed by the method conventions: Reflexivity, Symmetry and transitivity. Therefore, it is also important to note that unless you are willing to abandon the benefits of object-oriented abstraction (entry 10), you cannotcompareToinherit a class that can be instantiated with a new value component in the case of a retention convention. The same solution also applies. If you want to add a value component to the implementedComparableclass, do not inherit it; Write an unrelated class that contains the first class instance. Then provide a "view" method that returns the containing instance. This allows you to implement any method on the containing classcompareTo, while the client sees the instance of the containing class as an instance of a class as needed.
compareToThe last paragraph of the Convention is a strong suggestion, not a real requirement, but acompareTotest of equality imposed by the method of declaring the methods, which should normally return theequalssame result as the method. If this convention is adhered to, thecompareToorder in which the methods are applied is consideredequalsconsistent. If violated, the order relationship is consideredequalsinconsistent. AcompareToclass whose method imposesequalsa relationship with an inconsistent order is still valid, but an ordered collection containing the elements of that class may notCollectionobeySetMapThe general conventions of the corresponding collection interfaces (, or). This is because the common conventions for these interfaces areequalsdefined by methods, but the sorted collection iscompareToreplaced with the imposed equality testequals. If this happens, it's not a disaster, but it's still something to watch out for.
For example, considerBigDecimala classcompareTowithequalsinconsistent methods. If you create an emptyHashSetinstance and then addnew BigDecimal("1.0")and thennew BigDecimal("1.00")the collection will contain two elements, because theequalstwo instances added to the collection are not equal when compared to the methodBigDecimal. However, if you useTreeSetinsteadHashSetof performing the same procedure, the collection will contain only one element, becausecompareTotwoBigDecimalinstances are equal when compared using methods. (For more information, see theBigDecimaldocumentation.) )
WritingcompareTomethodsequalsare similar to writing methods, but there are some key differences. Because theComparableinterface is parameterized, thecompareTomethod is statically typed, so you do not need to enter a check or convert its parameters. If the argument is of the wrong type, then the call will not compile. If the argument is null, the call should throw anNullPointerExceptionexception, and once the method attempts to access its members, it throws the exception immediately.
In acompareTomethod, the order of the properties is compared instead of equal. To compare object reference properties, call the method recursivelycompareTo. If a property is not implementedComparable, or you need a nonstandard order, then use theComparatorinterface. You can write your own comparer or use an existing comparer, such as in the method of the class in item 10CaseInsensitiveStringcompareTo:
// Single-field Comparable with object reference field
public final class CaseInsensitiveString
????????implements Comparable<CaseInsensitiveString> {
????public int compareTo(CaseInsensitiveString cis) {
????????return String.CASE_INSENSITIVE_[ORDER.compare(s](http://ORDER.compare(s), cis.s);
????}
????... // Remainder omitted
}
Notice that theCaseInsensitiveStringclass implements theComparable <CaseInsensitiveString>interface. This means that theCaseInsensitiveStringreference can only be compared to anotherCaseInsensitiveStringreference. This is normal mode when declaring a class to implement anComparableinterface.
In the second edition of this book, it was recommended that if you compare attributes of an integral primitive type, use the relationship operator "<" and ">" for properties of the base type of floating-point type, useDouble.compareand [Float.comparestatic method. In Java 7, the static comparison method is added to all wrapper classes in Java.compareTousing the relational operator "<" and ">" in a method is verbose and error-prone, and is no longer recommended.
If a class has several important attributes, it is critical to compare their order. Start by comparing all the important attributes, starting with the most important attributes. If the comparison result is not 0 (0 equals), the comparison is complete; Just return the result. If the most important fields are equal, compare the next important property, and so on, until you find unequal properties or compare the remaining less important properties. The following is a method of the class in entry 11PhoneNumbercompareTo, which demonstrates this approach:
// Multiple-field Comparable with primitive fields
public int compareTo(PhoneNumber pn) {
????int result = [Short.compare(areaCode](http://Short.compare(areaCode), pn.areaCode);
????if (result == 0)??{
????????result = [Short.compare(prefix](http://Short.compare(prefix), pn.prefix);
????????if (result == 0)
????????????result = [Short.compare(lineNum](http://Short.compare(lineNum), pn.lineNum);
????}
????return result;
}
The interface in Java 8Comparatorprovides a series of comparator methods that allow the comparator to build smoothly. These comparators can be used to implementcompareTomethods, asComparablerequired by the interface. Many programmers prefer the simplicity of this approach, although its performance is not remarkable: the array of sorted instances on my machine isPhoneNumberabout 10% slower. When using this approach, consider using a static import of Java so that the comparer static method can be referenced by its simple name to make it clear and concise. Here's how toPhoneNumbercompareTouse the method:
// Comparable with comparator construction methods
private static final Comparator<PhoneNumber> COMPARATOR =
????????comparingInt((PhoneNumber pn) -> pn.areaCode)
??????????.thenComparingInt(pn -> pn.prefix)
??????????.thenComparingInt(pn -> pn.lineNum);
public int compareTo(PhoneNumber pn) {
????return [COMPARATOR.compare(this](http://COMPARATOR.compare(this), pn);
}
This implementation builds the comparer when the class is initialized, using two comparators to build the method. The first one is thecomparingIntmethod. It is a static method that uses a key extractor functional interface (? Key extractor function) as a parameter, maps an object reference to a key of type int, and returns a comparer for instances sorted by that key. In the previous example, thecomparingIntmethod uses a lambda expression, whichPhoneNumberextracts the area code from and returns oneComparator<PhoneNumber>that sorts the phone number according to their area code. Note that the lambda expression explicitly specifies the type of its input parameter(PhoneNumber pn). It turns out that in this case, the Java type inference function is not powerful enough to judge the type by itself, so we have to help it to compile the program.
If two phone number instances have the same area code, you need to refine the comparison further, which is the second comparator build method, which isthenComparingIntthe method. It isComparatoran instance method on which an int type key extractor functions as an argument and returns a comparer that first applies the original comparer and then uses the extracted keys to break the connection. You can call the T method multiple times in the way you like, resulting inthenComparingIna dictionary order. In the above example, we overlay two calls tothenComparingIntproduce a sort, its two-level key isprefix, and its third-level key islineNum. Note that we do not have to specify thethenComparingIntparameter type of the key extractor function interface that is passed to any one of the calls: Java type inference is smart enough to infer the type of a parameter by itself.
ComparatorClass has a complete build method. Forlonganddoublebasic types, there are corresponding similar tocomparingIntandthenComparingInt的methods, and theintversion of the method can also be applied to the value range less thanintthe type, such as theshorttype, asPhoneNumbershown in the instance.doublethe method for the version can also be used on thefloattype. This provides an overlay of all Java basic numeric types.
There are also comparator construction methods for object reference types. A static methodcomparinghas two overloaded methods. The first method uses the key extractor function interface and the natural order of the keys. The second method is the key extractor function interface and comparer, which are used to sort the keys.thenComparingThere are three overloads of the method. The first overload requires only one comparer and uses it to provide a level two ordering. The second overload requires only one key extractor function interface and uses the natural order of the keys as a two-level sort. The last overloaded method uses both a key extractor function interface and a comparer to be used on the extracted key.
Sometimes, you might seecompareToor thecomparemethod relies on the difference between two values, or negative if the first value is less than the second value, or zero if the value is equal to two, or positive if the first value is greater than the other. This is an example:
// BROKEN difference-based comparator - violates transitivity!
static Comparator<Object> hashCodeOrder = new Comparator<>() {
public int compare(Object o1, Object o2) {
return o1.hashCode() - o2.hashCode();
}
};
Don't use this technique! It may cause an integer maximum length overflow and the risk of IEEE 754 floating-point arithmetic distortion [JLS 15.20.1,15.21.1]. In addition, the resulting method is unlikely to be much faster than the method written using the techniques described above. Use staticcomparemethods:
**// Comparator based on static compare method**
static Comparator<Object> hashCodeOrder = new Comparator<>() {
????public int compare(Object o1, Object o2) {
????????return Integer.compare(o1.hashCode(), o2.hashCode());
????}
};
Or useComparatorthe build method:
// Comparator based on Comparator construction method
static Comparator<Object> hashCodeOrder =
Comparator.comparingInt(o -> o.hashCode());
All in all, whenever you implement a value class that has a reasonable sort, you should let the class implement anComparableinterface so that its instances can be easily sorted, searched, and used in a comparison-based collection.compareToAvoid using the "<" and ">" Operators when comparing field values in the implementation of a method. Instead, use a static method in the wrapper classcompareorComparatora build method in the interface.
Effective Java Third edition--14. Consider implementing the comparable interface