All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.landawn.abacus.util.Comparators Maven / Gradle / Ivy

Go to download

A general programming library in Java/Android. It's easy to learn and simple to use with concise and powerful APIs.

There is a newer version: 5.2.4
Show newest version
/*
 * Copyright (c) 2017, Haiyang Li.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.landawn.abacus.util;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.function.Function;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;

import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.util.function.ToBooleanFunction;
import com.landawn.abacus.util.function.ToByteFunction;
import com.landawn.abacus.util.function.ToCharFunction;
import com.landawn.abacus.util.function.ToFloatFunction;
import com.landawn.abacus.util.function.ToShortFunction;

/**
 *
 * Factory utility class for Comparator.
 *
 * @author haiyangl
 *
 */
public abstract class Comparators {

    @SuppressWarnings("rawtypes")
    static final Comparator NULL_FIRST_COMPARATOR = (a, b) -> a == null ? (b == null ? 0 : -1) : (b == null ? 1 : a.compareTo(b));

    @SuppressWarnings("rawtypes")
    static final Comparator NULL_LAST_COMPARATOR = (a, b) -> a == null ? (b == null ? 0 : 1) : (b == null ? -1 : a.compareTo(b));

    @SuppressWarnings("rawtypes")
    static final Comparator NATURAL_ORDER = NULL_FIRST_COMPARATOR;

    @SuppressWarnings("rawtypes")
    static final Comparator REVERSED_ORDER = Collections.reverseOrder(NATURAL_ORDER);

    static final Comparator COMPARING_IGNORE_CASE = N::compareIgnoreCase;

    static final Comparator COMPARING_BY_LENGTH = (a, b) -> (a == null ? 0 : a.length()) - (b == null ? 0 : b.length());

    @SuppressWarnings("rawtypes")
    static final Comparator COMPARING_BY_SIZE = (a, b) -> (a == null ? 0 : a.size()) - (b == null ? 0 : b.size());

    @SuppressWarnings("rawtypes")
    static final Comparator COMPARING_BY_MAP_SIZE = (a, b) -> (a == null ? 0 : a.size()) - (b == null ? 0 : b.size());

    Comparators() {
        // Singleton
    }

    public static final Comparator BOOLEAN_ARRAY_COMPARATOR = (a, b) -> {
        final int lenA = N.len(a);
        final int lenB = N.len(b);

        for (int i = 0, minLen = N.min(lenA, lenB); i < minLen; i++) {
            if (a[i] != b[i]) {
                return a[i] ? 1 : -1;
            }
        }

        return N.compare(lenA, lenB);
    };

    public static final Comparator CHAR_ARRAY_COMPARATOR = (a, b) -> {
        final int lenA = N.len(a);
        final int lenB = N.len(b);

        for (int i = 0, minLen = N.min(lenA, lenB); i < minLen; i++) {
            if (a[i] != b[i]) {
                return a[i] > b[i] ? 1 : -1;
            }
        }

        return N.compare(lenA, lenB);
    };

    public static final Comparator BYTE_ARRAY_COMPARATOR = (a, b) -> {
        final int lenA = N.len(a);
        final int lenB = N.len(b);

        for (int i = 0, minLen = N.min(lenA, lenB); i < minLen; i++) {
            if (a[i] != b[i]) {
                return a[i] > b[i] ? 1 : -1;
            }
        }

        return N.compare(lenA, lenB);
    };

    public static final Comparator SHORT_ARRAY_COMPARATOR = (a, b) -> {
        final int lenA = N.len(a);
        final int lenB = N.len(b);

        for (int i = 0, minLen = N.min(lenA, lenB); i < minLen; i++) {
            if (a[i] != b[i]) {
                return a[i] > b[i] ? 1 : -1;
            }
        }

        return N.compare(lenA, lenB);
    };

    public static final Comparator INT_ARRAY_COMPARATOR = (a, b) -> {
        final int lenA = N.len(a);
        final int lenB = N.len(b);

        for (int i = 0, minLen = N.min(lenA, lenB); i < minLen; i++) {
            if (a[i] != b[i]) {
                return a[i] > b[i] ? 1 : -1;
            }
        }

        return N.compare(lenA, lenB);
    };

    public static final Comparator LONG_ARRAY_COMPARATOR = (a, b) -> {
        final int lenA = N.len(a);
        final int lenB = N.len(b);

        for (int i = 0, minLen = N.min(lenA, lenB); i < minLen; i++) {
            if (a[i] != b[i]) {
                return a[i] > b[i] ? 1 : -1;
            }
        }

        return N.compare(lenA, lenB);
    };

    public static final Comparator FLOAT_ARRAY_COMPARATOR = (a, b) -> {
        final int lenA = N.len(a);
        final int lenB = N.len(b);
        int result = 0;

        for (int i = 0, minLen = N.min(lenA, lenB); i < minLen; i++) {
            result = Float.compare(a[i], b[i]);

            if (result != 0) {
                return result;
            }
        }

        return N.compare(lenA, lenB);
    };

    public static final Comparator DOUBLE_ARRAY_COMPARATOR = (a, b) -> {
        final int lenA = N.len(a);
        final int lenB = N.len(b);
        int result = 0;

        for (int i = 0, minLen = N.min(lenA, lenB); i < minLen; i++) {
            result = Double.compare(a[i], b[i]);

            if (result != 0) {
                return result;
            }
        }

        return N.compare(lenA, lenB);
    };

    public static final Comparator OBJECT_ARRAY_COMPARATOR = (a, b) -> {
        final int lenA = N.len(a);
        final int lenB = N.len(b);
        int result = 0;

        for (int i = 0, minLen = N.min(lenA, lenB); i < minLen; i++) {
            result = NATURAL_ORDER.compare(a[i], b[i]);

            if (result != 0) {
                return result;
            }
        }

        return N.compare(lenA, lenB);
    };

    @SuppressWarnings("rawtypes")
    public static final Comparator COLLECTION_COMPARATOR = (a, b) -> {
        if (N.isNullOrEmpty(a)) {
            return N.isNullOrEmpty(b) ? 0 : -1;
        } else if (N.isNullOrEmpty(b)) {
            return 1;
        }

        final Iterator iterA = a.iterator();
        final Iterator iterB = b.iterator();

        final int lenA = N.size(a);
        final int lenB = N.size(b);
        int result = 0;

        for (int i = 0, minLen = N.min(lenA, lenB); i < minLen; i++) {
            result = NATURAL_ORDER.compare(iterA.next(), iterB.next());

            if (result != 0) {
                return result;
            }
        }

        return N.compare(lenA, lenB);
    };

    /**
     * Same as {@code nullsFirst}.
     *
     * @param  
     * @return {@link #nullsFirst()}
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static  Comparator naturalOrder() {
        return NATURAL_ORDER;
    }

    /**
     *
     * @param 
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static  Comparator reversedOrder() {
        return REVERSED_ORDER;
    }

    /**
     *
     * @param 
     * @param cmp
     * @return
     */
    public static  Comparator reversedOrder(final Comparator cmp) {
        if (cmp == null || cmp == NATURAL_ORDER) {
            return REVERSED_ORDER;
        } else if (cmp == REVERSED_ORDER) {
            return NATURAL_ORDER;
        }

        return Collections.reverseOrder(cmp);
    }

    /**
     *
     * @param 
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static  Comparator nullsFirst() {
        return (Comparator) NULL_FIRST_COMPARATOR;
    }

    /**
     *
     * @param 
     * @param cmp
     * @return
     */
    public static  Comparator nullsFirst(final Comparator cmp) {
        if (cmp == null || cmp == NULL_FIRST_COMPARATOR) {
            return (Comparator) NULL_FIRST_COMPARATOR;
        }

        return (a, b) -> a == null ? (b == null ? 0 : -1) : (b == null ? 1 : cmp.compare(a, b));
    }

    @SuppressWarnings("rawtypes")
    private static final Comparator NULLS_FIRST_OR_ELSE_EQUAL = (a, b) -> a == null ? (b == null ? 0 : -1) : (b == null ? 1 : 0);

    /**
     *
     * @param 
     * @return
     */
    @Beta
    public static  Comparator nullsFirstOrElseEqual() {
        return NULLS_FIRST_OR_ELSE_EQUAL;
    }

    /**
     *
     * @param 
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static  Comparator nullsLast() {
        return (Comparator) NULL_LAST_COMPARATOR;
    }

    /**
     *
     * @param 
     * @param cmp
     * @return
     */
    public static  Comparator nullsLast(final Comparator cmp) {
        if (cmp == null || cmp == NULL_LAST_COMPARATOR) {
            return (Comparator) NULL_LAST_COMPARATOR;
        }

        return (a, b) -> a == null ? (b == null ? 0 : 1) : (b == null ? -1 : cmp.compare(a, b));
    }

    @SuppressWarnings("rawtypes")
    private static final Comparator NULLS_LAST_OR_ELSE_EQUAL = (a, b) -> a == null ? (b == null ? 0 : 1) : (b == null ? -1 : 0);

    /**
     *
     * @param 
     * @return
     */
    @Beta
    public static  Comparator nullsLastOrElseEqual() {
        return NULLS_LAST_OR_ELSE_EQUAL;
    }

    /**
     *
     * @param 
     * @param 
     * @param keyMapper
     * @return
     */
    public static > Comparator comparingBy(final Function keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> N.compare(keyMapper.apply(a), keyMapper.apply(b));
    }

    /**
     *
     * @param 
     * @param 
     * @param keyMapper
     * @return
     */
    @Beta
    public static > Comparator comparingByIfNotNullOrElseNullsFirst(final Function keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> a == null ? (b == null ? 0 : -1) : (b == null ? 1 : N.compare(keyMapper.apply(a), keyMapper.apply(b)));
    }

    /**
     *
     * @param 
     * @param 
     * @param keyMapper
     * @return
     */
    @Beta
    public static > Comparator comparingByIfNotNullOrElseNullsLast(final Function keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> a == null ? (b == null ? 0 : 1) : (b == null ? -1 : N.compare(keyMapper.apply(a), keyMapper.apply(b)));
    }

    /**
     * Reversed comparing by.
     *
     * @param 
     * @param 
     * @param keyMapper
     * @return
     */
    public static > Comparator reversedComparingBy(final Function keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> N.compare(keyMapper.apply(b), keyMapper.apply(a));
    }

    /**
     *
     * @param 
     * @param 
     * @param keyMapper
     * @return
     */
    @Beta
    public static > Comparator reversedComparingByIfNotNullOrElseNullsFirst(
            final Function keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> a == null ? (b == null ? 0 : -1) : (b == null ? 1 : N.compare(keyMapper.apply(b), keyMapper.apply(a)));
    }

    /**
     *
     * @param 
     * @param 
     * @param keyMapper
     * @return
     */
    @Beta
    public static > Comparator reversedComparingByIfNotNullOrElseNullsLast(
            final Function keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> a == null ? (b == null ? 0 : 1) : (b == null ? -1 : N.compare(keyMapper.apply(b), keyMapper.apply(a)));
    }

    /**
     *
     * @param 
     * @param 
     * @param keyMapper
     * @param keyComparator
     * @return
     */
    public static  Comparator comparingBy(final Function keyMapper, final Comparator keyComparator) {
        N.checkArgNotNull(keyMapper);
        N.checkArgNotNull(keyComparator);

        return (a, b) -> keyComparator.compare(keyMapper.apply(a), keyMapper.apply(b));
    }

    /**
     *
     * @param 
     * @param keyMapper
     * @return
     */
    public static  Comparator comparingBoolean(final ToBooleanFunction keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> Boolean.compare(keyMapper.applyAsBoolean(a), keyMapper.applyAsBoolean(b));
    }

    /**
     *
     * @param 
     * @param keyMapper
     * @return
     */
    public static  Comparator comparingChar(final ToCharFunction keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> Character.compare(keyMapper.applyAsChar(a), keyMapper.applyAsChar(b));
    }

    /**
     *
     * @param 
     * @param keyMapper
     * @return
     */
    public static  Comparator comparingByte(final ToByteFunction keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> Byte.compare(keyMapper.applyAsByte(a), keyMapper.applyAsByte(b));
    }

    /**
     *
     * @param 
     * @param keyMapper
     * @return
     */
    public static  Comparator comparingShort(final ToShortFunction keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> Short.compare(keyMapper.applyAsShort(a), keyMapper.applyAsShort(b));
    }

    /**
     *
     * @param 
     * @param keyMapper
     * @return
     */
    public static  Comparator comparingInt(final ToIntFunction keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> Integer.compare(keyMapper.applyAsInt(a), keyMapper.applyAsInt(b));
    }

    /**
     *
     * @param 
     * @param keyMapper
     * @return
     */
    public static  Comparator comparingLong(final ToLongFunction keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> Long.compare(keyMapper.applyAsLong(a), keyMapper.applyAsLong(b));
    }

    /**
     *
     * @param 
     * @param keyMapper
     * @return
     */
    public static  Comparator comparingFloat(final ToFloatFunction keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> Float.compare(keyMapper.applyAsFloat(a), keyMapper.applyAsFloat(b));
    }

    /**
     *
     * @param 
     * @param keyMapper
     * @return
     */
    public static  Comparator comparingDouble(final ToDoubleFunction keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> Double.compare(keyMapper.applyAsDouble(a), keyMapper.applyAsDouble(b));
    }

    /**
     * Comparing ignore case.
     *
     * @return
     */
    public static Comparator comparingIgnoreCase() {
        return COMPARING_IGNORE_CASE;
    }

    /**
     * Comparing ignore case.
     *
     * @param 
     * @param keyMapper
     * @return
     */
    public static  Comparator comparingIgnoreCase(final Function keyMapper) {
        N.checkArgNotNull(keyMapper);

        return (a, b) -> N.compareIgnoreCase(keyMapper.apply(a), keyMapper.apply(b));
    }

    /**
     * Comparing by key.
     *
     * @param  the key type
     * @param  the value type
     * @return
     */
    public static , V> Comparator> comparingByKey() {
        return (a, b) -> N.compare(a.getKey(), b.getKey());
    }

    /**
     * Reversed comparing by key.
     *
     * @param  the key type
     * @param  the value type
     * @return
     */
    public static , V> Comparator> reversedComparingByKey() {
        return (a, b) -> N.compare(b.getKey(), a.getKey());
    }

    /**
     * Comparing by value.
     *
     * @param  the key type
     * @param  the value type
     * @return
     */
    public static > Comparator> comparingByValue() {
        return (a, b) -> N.compare(a.getValue(), b.getValue());
    }

    /**
     * Reversed comparing by value.
     *
     * @param  the key type
     * @param  the value type
     * @return
     */
    public static > Comparator> reversedComparingByValue() {
        return (a, b) -> N.compare(b.getValue(), a.getValue());
    }

    /**
     * Comparing by key.
     *
     * @param  the key type
     * @param  the value type
     * @param cmp
     * @return
     */
    public static  Comparator> comparingByKey(final Comparator cmp) {
        N.checkArgNotNull(cmp);

        return (a, b) -> cmp.compare(a.getKey(), b.getKey());
    }

    /**
     * Comparing by value.
     *
     * @param  the key type
     * @param  the value type
     * @param cmp
     * @return
     */
    public static  Comparator> comparingByValue(final Comparator cmp) {
        N.checkArgNotNull(cmp);

        return (a, b) -> cmp.compare(a.getValue(), b.getValue());
    }

    /**
     * Reversed comparing by key.
     *
     * @param  the key type
     * @param  the value type
     * @param cmp
     * @return
     */
    @Beta
    public static  Comparator> reversedComparingByKey(final Comparator cmp) {
        N.checkArgNotNull(cmp);

        return (a, b) -> cmp.compare(b.getKey(), a.getKey());
    }

    /**
     * Reversed comparing by value.
     *
     * @param  the key type
     * @param  the value type
     * @param cmp
     * @return
     */
    @Beta
    public static  Comparator> reversedComparingByValue(final Comparator cmp) {
        N.checkArgNotNull(cmp);

        return (a, b) -> cmp.compare(b.getValue(), a.getValue());
    }

    /**
     * Comparing by length.
     *
     * @param 
     * @return
     */
    public static  Comparator comparingByLength() {
        return (Comparator) COMPARING_BY_LENGTH;
    }

    static final Comparator COMPARING_BY_ARRAY_LENGTH = (a, b) -> (a == null ? 0 : Array.getLength(a)) - (b == null ? 0 : Array.getLength(b));

    /**
     * Comparing by array length.
     *
     * @param 
     * @return
     */
    public static  Comparator comparingByArrayLength() {
        return (Comparator) COMPARING_BY_ARRAY_LENGTH;
    }

    /**
     * Comparing by collection size.
     *
     * @param 
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static  Comparator comparingBySize() {
        return (Comparator) COMPARING_BY_SIZE;
    }

    /**
     * Comparing by map size.
     *
     * @param 
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static  Comparator comparingByMapSize() {
        return (Comparator) COMPARING_BY_MAP_SIZE;
    }

    /**
     * 
     *
     * @param  
     * @param cmp 
     * @return 
     */
    public static  Comparator comparingArray(final Comparator cmp) {
        N.checkArgNotNull(cmp);

        return (a, b) -> {
            if (N.isNullOrEmpty(a)) {
                return N.isNullOrEmpty(b) ? 0 : -1;
            } else if (N.isNullOrEmpty(b)) {
                return 1;
            }

            final int lenA = N.len(a);
            final int lenB = N.len(b);
            int result = 0;

            for (int i = 0, minLen = N.min(lenA, lenB); i < minLen; i++) {
                result = cmp.compare(a[i], b[i]);

                if (result != 0) {
                    return result;
                }
            }

            return N.compare(lenA, lenB);
        };
    }

    /**
     * 
     *
     * @param  
     * @param  
     * @param cmp 
     * @return 
     */
    public static > Comparator comparingCollection(final Comparator cmp) {
        N.checkArgNotNull(cmp);

        return (a, b) -> {
            if (N.isNullOrEmpty(a)) {
                return N.isNullOrEmpty(b) ? 0 : -1;
            } else if (N.isNullOrEmpty(b)) {
                return 1;
            }

            final Iterator iterA = a.iterator();
            final Iterator iterB = b.iterator();

            final int sizeA = N.size(a);
            final int sizeB = N.size(b);
            int result = 0;

            for (int i = 0, minLen = N.min(sizeA, sizeB); i < minLen; i++) {
                result = cmp.compare(iterA.next(), iterB.next());

                if (result != 0) {
                    return result;
                }
            }

            return N.compare(sizeA, sizeB);
        };
    }

    /**
     * 
     *
     * @param  
     * @param  
     * @param cmp 
     * @return 
     */
    public static > Comparator comparingIterator(final Comparator cmp) {
        N.checkArgNotNull(cmp);

        return (a, b) -> {
            if (N.isNullOrEmpty(a)) {
                return N.isNullOrEmpty(b) ? 0 : -1;
            } else if (N.isNullOrEmpty(b)) {
                return 1;
            }

            final Iterator iterA = a;
            final Iterator iterB = b;

            int result = 0;

            while (iterA.hasNext() && iterB.hasNext()) {
                result = cmp.compare(iterA.next(), iterB.next());

                if (result != 0) {
                    return result;
                }
            }

            return iterA.hasNext() ? 1 : (iterB.hasNext() ? -1 : 0);
        };
    }
}