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

de.unkrig.commons.util.collections.CollectionUtil Maven / Gradle / Ivy


/*
 * de.unkrig.commons - A general-purpose Java class library
 *
 * Copyright (c) 2014, Arno Unkrig
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 *       following disclaimer in the documentation and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package de.unkrig.commons.util.collections;

import java.io.Serializable;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;

import de.unkrig.commons.lang.AssertionUtil;
import de.unkrig.commons.nullanalysis.NotNullByDefault;
import de.unkrig.commons.nullanalysis.Nullable;

/**
 * {@code java.util.collection}-related utility methods.
 */
public final
class CollectionUtil {

    static { AssertionUtil.enableAssertionsForThisClass(); }

    private
    CollectionUtil() {}

    /**
     * Removes and returns the first element of the given collection. This is also known as the 'shift' operation.
     *
     * @return {@code null} iff the collection is empty
     */
    @Nullable public static  T
    removeFirstFrom(Collection subject) {
        Iterator it = subject.iterator();
        if (!it.hasNext()) return null;
        T result = it.next();
        it.remove();
        return result;
    }

    /**
     * Returns an unmodifiable map, mapping the given key-value pairs. {@code null} keys and {@code null} values are
     * supported.
     *
     * @param keyValuePairs                   An alternating sequence of keys and values
     * @throws ArrayIndexOutOfBoundsException The length of {@code keyValuePairs} is odd
     * @throws IllegalArgumentException       Two of the keys are equal
     */
    @SuppressWarnings("unchecked") public static  Map
    map(Object... keyValuePairs) {

        int n = keyValuePairs.length;
        if ((n & 1) == 1) throw new ArrayIndexOutOfBoundsException(n);

        if (n == 0) return Collections.emptyMap();
        if (n == 2) return Collections.singletonMap((K) keyValuePairs[0], (V) keyValuePairs[1]);

        Map result = (
            n <= 8
            ? new LinearMap(n / 2)
            : new HashMap(n)
        );
        for (int i = 0; i < n;) {
            if (result.put((K) keyValuePairs[i++], (V) keyValuePairs[i++]) != null) {
                throw new IllegalArgumentException("Duplicate key '" + keyValuePairs[i - 2]);
            }
        }
        return Collections.unmodifiableMap(result);
    }

    /**
     * Returns an unmodifiable map, mapping the given key-value pairs. {@code null} keys and {@code null} values are
     * supported.
     *
     * @throws ArrayIndexOutOfBoundsException The length of {@code keyValuePairs} is odd
     * @throws IllegalArgumentException       Two of the keys are equal
     */
    public static  Map
    map(K[] keys, V[] values) {

        int n = keys.length;
        assert n == values.length;

        if (n == 0) return Collections.emptyMap();
        if (n == 1) return Collections.singletonMap(keys[0], values[0]);

        Map result = (
            n <= 4
            ? new LinearMap(n)
            : new HashMap(2 * n)
        );
        for (int i = 0; i < n; i++) {
            if (result.put(keys[i], values[i]) != null) {
                throw new IllegalArgumentException("Duplicate key '" + keys[i]);
            }
        }
        return Collections.unmodifiableMap(result);
    }

    /**
     * Desperately missing from {@code java.util.Collections}.
     */
    @SuppressWarnings("rawtypes") public static final SortedMap
    EMPTY_SORTED_MAP = new EmptySortedMap();

    /**
     * Desperately missing from {@code java.util.Collections}.
     */
    @SuppressWarnings("unchecked") public static  SortedMap
    emptySortedMap() { return CollectionUtil.EMPTY_SORTED_MAP; }

    @NotNullByDefault(false) @SuppressWarnings("rawtypes") private static
    class EmptySortedMap extends AbstractMap implements SortedMap, Serializable {

        private static final long serialVersionUID = 1;

        @Override public Comparator comparator()                         { return null; }
        @Override public SortedMap  subMap(Object fromKey, Object toKey) { return CollectionUtil.EMPTY_SORTED_MAP; }
        @Override public SortedMap  headMap(Object toKey)                { return CollectionUtil.EMPTY_SORTED_MAP; }
        @Override public SortedMap  tailMap(Object fromKey)              { return CollectionUtil.EMPTY_SORTED_MAP; }
        @Override public Object     firstKey()                           { throw new NoSuchElementException(); }
        @Override public Object     lastKey()                            { throw new NoSuchElementException(); }
        @Override public int        size()                               { return 0; }
        @Override public boolean    isEmpty()                            { return true; }
        @Override public boolean    containsKey(Object key)              { return false; }
        @Override public boolean    containsValue(Object value)          { return false; }
        @Override public Object     get(Object key)                      { return null; }
        @Override public Set        keySet()                             { return Collections.EMPTY_SET; }
        @Override public Collection values()                             { return Collections.EMPTY_SET; }
        @Override public Set        entrySet()                           { return Collections.EMPTY_SET; }
        @Override public boolean    equals(Object o)                     { return (o instanceof SortedMap) && ((SortedMap) o).size() == 0; } // SUPPRESS CHECKSTYLE LineLength
        @Override public int        hashCode()                           { return 0; }
    }

    /**
     * Desperately missing from {@code java.util.Collections}.
     */
    @SuppressWarnings("rawtypes") public static final SortedSet
    EMPTY_SORTED_SET = new EmptySortedSet();

    /**
     * Desperately missing from {@code java.util.Collections}.
     */
    @SuppressWarnings("unchecked") public static  SortedSet
    emptySortedSet() { return CollectionUtil.EMPTY_SORTED_SET; }

    @NotNullByDefault(false) @SuppressWarnings("rawtypes") private static
    class EmptySortedSet extends AbstractSet implements SortedSet, Serializable {

        private static final long serialVersionUID = 1L;

        @Override public Iterator   iterator()                     { return CollectionUtil.AT_END; }
        @Override public int        size()                         { return 0; }
        @Override public boolean    isEmpty()                      { return true; }
        @Override public boolean    contains(Object obj)           { return false; }
        @Override public Comparator comparator()                   { return null; }
        @Override public SortedSet  subSet(Object from, Object to) { return CollectionUtil.EMPTY_SORTED_SET; }
        @Override public SortedSet  headSet(Object toElement)      { return CollectionUtil.EMPTY_SORTED_SET; }
        @Override public SortedSet  tailSet(Object fromElement)    { return CollectionUtil.EMPTY_SORTED_SET; }
        @Override public Object     first()                        { throw new NoSuchElementException(); }
        @Override public Object     last()                         { throw new NoSuchElementException(); }
        @Override public boolean    equals(Object o)               { return (o instanceof SortedSet) && ((SortedSet) o).size() == 0; } // SUPPRESS CHECKSTYLE LineLength
        @Override public int        hashCode()                     { return 0; }
    }

    /**
     * An iterator which is at its end.
     */
    @SuppressWarnings("rawtypes") public static final Iterator AT_END = new Iterator() {
        @Override public boolean hasNext() { return false; }
        @Override public Object  next()    { throw new NoSuchElementException(); }
        @Override public void    remove()  { throw new UnsupportedOperationException(); }
    };

    /**
     * Returns an unmodifiable collection containing the elements of the given collection, but sorted by
     * their "natural ordering".
     */
    @SuppressWarnings("unchecked") public static  Collection
    sorted(Collection collection) {

        Object[] a = collection.toArray();

        Arrays.sort(a);

        return (Collection) Arrays.asList(a);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy