package javolution37.javolution.util;

import javolution37.javolution.Configuration;
import javolution37.javolution.lang.Text;

import java.io.Serializable;
import java.lang.CharSequence;
import java.lang.Comparable;
import java.util.Comparator;

/**
 * 

This class represents a comparator to be used for equality as well as * for ordering; instances of this class provide a hashcode function * consistent with equal (if two objects {@link #areEqual * are equal}, they have the same {@link #hashCodeOf hashcode}), * equality with null values is supported.

* *

{@link FastComparator} can be employed with {@link FastMap} (e.g. custom * key comparators for identity maps, value retrieval using keys of a * different class that the map keys) or with {@link FastCollection} * classes.

* * @author Jean-Marie Dautelle * @version 3.4, September 20, 2005 */ public abstract class FastComparator implements Comparator , Serializable { /** * Holds the default object comparator; rehash is performed if the * system hash code (platform dependent) is not evenly distributed. * @see * Javolution Configuration */ public static final FastComparator DEFAULT = new Default(Configuration .isPoorSystemHash()); static class Default extends FastComparator { private boolean _isPoorSystemHash; public Default(boolean isPoorSystemHash) { _isPoorSystemHash = isPoorSystemHash; } public int hashCodeOf(Object obj) { return (_isPoorSystemHash ? REHASH.hashCodeOf(obj) : obj.hashCode()); } public boolean areEqual(Object o1, Object o2) { return (o1 == null) ? (o2 == null) : (o1 == o2) || o1.equals(o2); } public int compare(Object o1, Object o2) { return ((Comparable) o1).compareTo(o2); } public String toString() { return "default"; } }; /** * Holds the direct object comparator; no rehash is performed. * Two objects o1 and o2 are considered {@link #areEqual equal} if and * only if o1.equals(o2). The {@link #compare} method * throws {@link ClassCastException} if the specified objects are not * {@link Comparable}. */ public static final FastComparator DIRECT = new Direct(); static class Direct extends FastComparator { public int hashCodeOf(Object obj) { return obj.hashCode(); } public boolean areEqual(Object o1, Object o2) { return (o1 == null) ? (o2 == null) : (o1 == o2) || o1.equals(o2); } public int compare(Object o1, Object o2) { return ((Comparable) o1).compareTo(o2); } public String toString() { return "direct"; } }; /** * Holds the comparator for objects with uneven hash distribution; objects * hashcodes are rehashed. Two objects o1 and o2 are considered * {@link #areEqual equal} if and only if o1.equals(o2). * The {@link #compare} method throws {@link ClassCastException} if the * specified objects are not {@link Comparable}. */ public static final FastComparator REHASH = new Rehash(); static class Rehash extends FastComparator { public int hashCodeOf(Object obj) { // Formula identical java.util.HashMap to ensures // similar behavior for ill-conditioned hashcode keys. int h = obj.hashCode(); h += ~(h << 9); h ^= (h >>> 14); h += (h << 4); return h ^ (h >>> 10); } public boolean areEqual(Object o1, Object o2) { return (o1 == null) ? (o2 == null) : (o1 == o2) || o1.equals(o2); } public int compare(Object o1, Object o2) { return ((Comparable) o1).compareTo(o2); } public String toString() { return "rehash"; } }; /** * Holds the identity comparator; poorly distributed system hashcodes are * rehashed. Two objects o1 and o2 are considered {@link #areEqual equal} * if and only if (o1 == o2). The {@link #compare} method * throws {@link ClassCastException} if the specified objects are not * {@link Comparable}. */ public static final FastComparator IDENTITY = new Identity(); static class Identity extends FastComparator { private boolean _rehash = !Configuration.isPoorSystemHash(); public int hashCodeOf(Object obj) { int h = System.identityHashCode(obj); if (!_rehash) return h; h += ~(h << 9); h ^= (h >>> 14); h += (h << 4); return h ^ (h >>> 10); } public boolean areEqual(Object o1, Object o2) { return o1 == o2; } public int compare(Object o1, Object o2) { return ((Comparable) o1).compareTo(o2); } public String toString() { return "identity"; } }; /** * Holds a lexicographic comparator for any {@link CharSequence} or * {@link String} instances. * Two objects are considered {@link #areEqual equal} if and only if they * represents the same character sequence). The hashcode is calculated * using the following formula (same as for java.lang.String): * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] */ public static final FastComparator LEXICAL = new Lexical(); static class Lexical extends FastComparator { public int hashCodeOf(Object obj) { if ((obj instanceof String) || (obj instanceof Text)) return obj.hashCode(); CharSequence chars = (CharSequence) obj; int h = 0; final int length = chars.length(); for (int i = 0; i < length;) { h = 31 * h + chars.charAt(i++); } return h; } public boolean areEqual(Object o1, Object o2) { if ((o1 instanceof String) && (o2 instanceof String)) return o1.equals(o2); if ((o1 instanceof CharSequence) && (o2 instanceof String)) { final CharSequence csq = (CharSequence) o1; final String str = (String) o2; final int length = str.length(); if (csq.length() != length) return false; for (int i = 0; i < length;) { if (str.charAt(i) != csq.charAt(i++)) return false; } return true; } if ((o1 instanceof String) && (o2 instanceof CharSequence)) { final CharSequence csq = (CharSequence) o2; final String str = (String) o1; final int length = str.length(); if (csq.length() != length) return false; for (int i = 0; i < length;) { if (str.charAt(i) != csq.charAt(i++)) return false; } return true; } final CharSequence csq1 = (CharSequence) o1; final CharSequence csq2 = (CharSequence) o2; final int length = csq1.length(); if (csq2.length() != length) return false; for (int i = 0; i < length;) { if (csq1.charAt(i) != csq2.charAt(i++)) return false; } return true; } public int compare(Object left, Object right) { if (left instanceof String) { if (right instanceof String) return ((String) left).compareTo((String) right); // Right must be a CharSequence. String seq1 = (String) left; CharSequence seq2 = (CharSequence) right; int i = 0; int n = Math.min(seq1.length(), seq2.length()); while (n-- != 0) { char c1 = seq1.charAt(i); char c2 = seq2.charAt(i++); if (c1 != c2) { return c1 - c2; } } return seq1.length() - seq2.length(); } if (right instanceof String) return -compare(right, left); // Both are CharSequence. CharSequence seq1 = (CharSequence) left; CharSequence seq2 = (CharSequence) right; int i = 0; int n = Math.min(seq1.length(), seq2.length()); while (n-- != 0) { char c1 = seq1.charAt(i); char c2 = seq2.charAt(i++); if (c1 != c2) { return c1 - c2; } } return seq1.length() - seq2.length(); } public String toString() { return "lexical"; } }; /** * Returns the hash code for the specified object (consistent with * {@link #areEqual}). Two objects considered {@link #areEqual equal} have * the same hash code. * * @param obj the object to return the hashcode for. * @return the hashcode for the specified object. * @throws NullPointerException if the specified object is * null. */ public abstract int hashCodeOf( T obj); /** * Indicates if the specified objects can be considered equal. * * @param o1 the first object (or null). * @param o2 the second object (or null). * @return true if both objects are considered equal; * false otherwise. */ public abstract boolean areEqual( T o1, T o2); /** * Compares the specified objects for order. Returns a negative integer, * zero, or a positive integer as the first argument is less than, equal to, * or greater than the second. * * @param o1 the first object. * @param o2 the second object. * @return a negative integer, zero, or a positive integer as the first * argument is less than, equal to, or greater than the second. * @throws NullPointerException if any of the specified object is * null. */ public abstract int compare( T o1, T o2); }

Ads help maintain this website.