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

com.helger.commons.collection.impl.ICommonsMap Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2014-2024 Philip Helger (www.helger.com)
 * philip[at]helger[dot]com
 *
 * 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.helger.commons.collection.impl;

import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.CodingStyleguideUnaware;
import com.helger.commons.annotation.ReturnsMutableCopy;
import com.helger.commons.collection.CollectionHelper;
import com.helger.commons.collection.map.MapEntry;
import com.helger.commons.lang.ICloneable;
import com.helger.commons.state.EChange;

/**
 * A special {@link Map} interface with extended functionality
 *
 * @author Philip Helger
 * @param 
 *        Map key type
 * @param 
 *        Map value type
 */
public interface ICommonsMap  extends Map , ICloneable >
{
  /**
   * Create a new empty map. Overwrite this if you don't want to use
   * {@link CommonsHashMap}.
   *
   * @return A new empty map. Never null.
   * @param 
   *        Map key type
   * @param 
   *        Map value type
   */
  @Nonnull
  @ReturnsMutableCopy
  default  ICommonsMap  createInstance ()
  {
    return new CommonsHashMap <> ();
  }

  /**
   * @return A new non-null set with all keys.
   * @see #keySet()
   * @see #copyOfKeySet(Predicate)
   */
  @Nonnull
  @ReturnsMutableCopy
  default ICommonsSet  copyOfKeySet ()
  {
    return new CommonsHashSet <> (keySet ());
  }

  /**
   * Create a copy of all values matching the passed filter.
   *
   * @param aFilter
   *        The filter to be applied. May be null.
   * @return A new non-null set with all matching keys.
   * @see #keySet()
   * @see #copyOfKeySet()
   */
  @Nonnull
  @ReturnsMutableCopy
  default ICommonsSet  copyOfKeySet (@Nullable final Predicate  aFilter)
  {
    if (aFilter == null)
      return copyOfKeySet ();
    return CollectionHelper.newSet (keySet (), aFilter);
  }

  /**
   * @return A new non-null set with all values.
   * @see #values()
   * @see #copyOfValues(Predicate)
   * @see #copyOfValuesMapped(Function)
   * @see #copyOfValuesMapped(Predicate, Function)
   */
  @Nonnull
  @ReturnsMutableCopy
  default ICommonsList  copyOfValues ()
  {
    return new CommonsArrayList <> (values ());
  }

  /**
   * Create a copy of all values matching the passed filter.
   *
   * @param aFilter
   *        The filter to be applied. May be null.
   * @return A new list with all matching values. If no filter is provided the
   *         returned value is identical as of {@link #copyOfValues()}
   * @see #values()
   * @see #copyOfValues()
   * @see #copyOfValuesMapped(Function)
   * @see #copyOfValuesMapped(Predicate, Function)
   */
  @Nonnull
  @ReturnsMutableCopy
  default ICommonsList  copyOfValues (@Nullable final Predicate  aFilter)
  {
    if (aFilter == null)
      return copyOfValues ();
    return CollectionHelper.newList (values (), aFilter);
  }

  /**
   * Create a copy of all values after applying the passed mapping function.
   *
   * @param aMapper
   *        The mapping function to be applied. May not be null.
   * @return A new list with all mapped values.
   * @param 
   *        The destination type to be mapped to
   * @see #values()
   * @see #copyOfValues()
   * @see #copyOfValuesMapped(Function)
   * @see #copyOfValuesMapped(Predicate, Function)
   */
  @Nonnull
  @ReturnsMutableCopy
  default  ICommonsList  copyOfValuesMapped (@Nonnull final Function  aMapper)
  {
    return CollectionHelper.newListMapped (values (), aMapper);
  }

  /**
   * Create a copy of all values matching the passed filter which are then
   * converted using the provided function.
   *
   * @param aFilter
   *        The filter to be applied. May be null.
   * @param aMapper
   *        The mapping function to be applied. May not be null.
   * @return A new list with all matching converted values. If no filter is
   *         provided the returned value is identical as of
   *         {@link #copyOfValuesMapped(Function)}
   * @param 
   *        The destination type to be mapped to
   * @see #values()
   * @see #copyOfValues()
   * @see #copyOfValues(Predicate)
   * @see #copyOfValuesMapped(Predicate, Function)
   */
  @Nonnull
  @ReturnsMutableCopy
  default  ICommonsList  copyOfValuesMapped (@Nullable final Predicate  aFilter,
                                                               @Nonnull final Function  aMapper)
  {
    if (aFilter == null)
      return copyOfValuesMapped (aMapper);
    return CollectionHelper.newListMapped (values (), aFilter, aMapper);
  }

  /**
   * @return A new non-null copy of the entry set.
   */
  @Nonnull
  @ReturnsMutableCopy
  default ICommonsSet > copyOfEntrySet ()
  {
    // This is contained, because "Map.Entry" instance may get reused internally
    final ICommonsSet > ret = new CommonsHashSet <> (size ());
    for (final Map.Entry  aEntry : entrySet ())
      ret.add (new MapEntry <> (aEntry));
    return ret;
  }

  /**
   * @return true if the map is not empty, false
   *         otherwise.
   */
  default boolean isNotEmpty ()
  {
    return !isEmpty ();
  }

  /**
   * Get the first element of this map or null.
   *
   * @return null if the map is empty, the first element otherwise.
   * @see #getFirstEntry(java.util.Map.Entry)
   */
  @Nullable
  default Map.Entry  getFirstEntry ()
  {
    return getFirstEntry (null);
  }

  /**
   * Get the first element of this map or the provided default value.
   *
   * @param aDefault
   *        The default value to be returned if this map is empty. May be
   *        null.
   * @return The provided default value if the map is empty, the first entry
   *         otherwise.
   * @see #getFirstEntry()
   */
  @Nullable
  default Map.Entry  getFirstEntry (@Nullable final Map.Entry  aDefault)
  {
    return isEmpty () ? aDefault : entrySet ().iterator ().next ();
  }

  /**
   * Get the first key of this map or null.
   *
   * @return null if the map is empty, the first key otherwise.
   * @see #getFirstKey(Object)
   */
  @Nullable
  default KEYTYPE getFirstKey ()
  {
    return getFirstKey (null);
  }

  /**
   * Get the first key of this map or the provided default value.
   *
   * @param aDefault
   *        The default value to be returned if this map is empty. May be
   *        null.
   * @return The provided default value if the map is empty, the first key
   *         otherwise.
   * @see #getFirstKey()
   */
  @Nullable
  default KEYTYPE getFirstKey (@Nullable final KEYTYPE aDefault)
  {
    return isEmpty () ? aDefault : keySet ().iterator ().next ();
  }

  /**
   * Get the first value of this map or null.
   *
   * @return null if the map is empty, the first value otherwise.
   * @see #getFirstValue(Object)
   */
  @Nullable
  default VALUETYPE getFirstValue ()
  {
    return getFirstValue (null);
  }

  /**
   * Get the first value of this map or the provided default value.
   *
   * @param aDefault
   *        The default value to be returned if this map is empty. May be
   *        null.
   * @return The provided default value if the map is empty, the first value
   *         otherwise.
   * @see #getFirstValue()
   */
  @Nullable
  default VALUETYPE getFirstValue (@Nullable final VALUETYPE aDefault)
  {
    return isEmpty () ? aDefault : values ().iterator ().next ();
  }

  /**
   * Find the first entry that matches the passed filter.
   *
   * @param aFilter
   *        The filter to be applied. May be null.
   * @return null if no matching element was found. I no filter was
   *         provided, the result is the same as {@link #getFirstEntry()}.
   */
  @Nullable
  default Map.Entry  findFirstEntry (@Nullable final Predicate > aFilter)
  {
    return CollectionHelper.findFirst (entrySet (), aFilter);
  }

  /**
   * Find the first key that matches the passed filter.
   *
   * @param aFilter
   *        The filter to be applied. May be null.
   * @return null if no matching element was found. I no filter was
   *         provided, the result is the same as {@link #getFirstKey()}.
   */
  @Nullable
  default KEYTYPE findFirstKey (@Nullable final Predicate > aFilter)
  {
    final Map.Entry  aEntry = findFirstEntry (aFilter);
    return aEntry == null ? null : aEntry.getKey ();
  }

  /**
   * Find the first value that matches the passed filter.
   *
   * @param aFilter
   *        The filter to be applied. May be null.
   * @return null if no matching element was found. I no filter was
   *         provided, the result is the same as {@link #getFirstValue()}.
   */
  @Nullable
  default VALUETYPE findFirstValue (@Nullable final Predicate > aFilter)
  {
    final Map.Entry  aEntry = findFirstEntry (aFilter);
    return aEntry == null ? null : aEntry.getValue ();
  }

  /**
   * Check if at least one entry matches the passed filter.
   *
   * @param aFilter
   *        The filter to be applied. May be null.
   * @return true if the map is not empty and contains at least one
   *         element matching the filter, false otherwise. If no
   *         filter is provided the return value is identical to
   *         {@link #isNotEmpty()}.
   */
  default boolean containsAnyEntry (@Nullable final Predicate > aFilter)
  {
    return CollectionHelper.containsAny (entrySet (), aFilter);
  }

  /**
   * Check if at least one key matches the passed filter.
   *
   * @param aFilter
   *        The filter to be applied. May be null.
   * @return true if the map is not empty and contains at least one
   *         key matching the filter, false otherwise. If no filter
   *         is provided the return value is identical to {@link #isNotEmpty()}.
   */
  default boolean containsAnyKey (@Nullable final Predicate  aFilter)
  {
    return CollectionHelper.containsAny (keySet (), aFilter);
  }

  /**
   * Check if at least one value matches the passed filter.
   *
   * @param aFilter
   *        The filter to be applied. May be null.
   * @return true if the map is not empty and contains at least one
   *         value matching the filter, false otherwise. If no
   *         filter is provided the return value is identical to
   *         {@link #isNotEmpty()}.
   */
  default boolean containsAnyValue (@Nullable final Predicate  aFilter)
  {
    return CollectionHelper.containsAny (values (), aFilter);
  }

  /**
   * Invoke the provided consumer on each key.
   *
   * @param aConsumer
   *        The consumer to be invoked. May not be null.
   */
  default void forEachKey (@Nonnull final Consumer  aConsumer)
  {
    forEach ( (k, v) -> aConsumer.accept (k));
  }

  /**
   * Invoke the provided consumer on each value.
   *
   * @param aConsumer
   *        The consumer to be invoked. May not be null.
   */
  default void forEachValue (@Nonnull final Consumer  aConsumer)
  {
    forEach ( (k, v) -> aConsumer.accept (v));
  }

  /**
   * Invoke the provided consumer on each entry (pair of key and value) that
   * matches the provided filter.
   *
   * @param aFilter
   *        The filter to be applied. May be null.
   * @param aConsumer
   *        The consumer to be invoked. May not be null.
   * @see #forEach(BiConsumer)
   */
  default void forEach (@Nullable final BiPredicate  aFilter,
                        @Nonnull final BiConsumer  aConsumer)
  {
    if (aFilter == null)
      forEach (aConsumer);
    else
    {
      // Use eventually present more performant forEach
      forEach ( (k, v) -> {
        if (aFilter.test (k, v))
          aConsumer.accept (k, v);
      });
    }
  }

  /**
   * Invoke the provided consumer on each key that matches the provided filter.
   *
   * @param aFilter
   *        The filter to be applied. May be null.
   * @param aConsumer
   *        The consumer to be invoked. May not be null.
   * @see #forEachKey(Consumer)
   * @see #forEach(BiPredicate, BiConsumer)
   */
  default void forEachKey (@Nullable final Predicate  aFilter, @Nonnull final Consumer  aConsumer)
  {
    if (aFilter == null)
      forEachKey (aConsumer);
    else
      forEach ( (k, v) -> aFilter.test (k), (k, v) -> aConsumer.accept (k));
  }

  /**
   * Invoke the provided consumer on each value that matches the provided
   * filter.
   *
   * @param aFilter
   *        The filter to be applied. May be null.
   * @param aConsumer
   *        The consumer to be invoked. May not be null.
   * @see #forEachValue(Consumer)
   * @see #forEach(BiPredicate, BiConsumer)
   */
  default void forEachValue (@Nullable final Predicate  aFilter, @Nonnull final Consumer  aConsumer)
  {
    if (aFilter == null)
      forEachValue (aConsumer);
    else
      forEach ( (k, v) -> aFilter.test (v), (k, v) -> aConsumer.accept (v));
  }

  /**
   * Get the map sorted by its keys. The comparison order is defined by the
   * passed comparator object.
   *
   * @param aKeyComparator
   *        The comparator to be used. May not be null.
   * @return the sorted map and never null.
   */
  @Nonnull
  @ReturnsMutableCopy
  default ICommonsOrderedMap  getSortedByKey (@Nonnull final Comparator  aKeyComparator)
  {
    return CollectionHelper.getSortedByKey (this, aKeyComparator);
  }

  /**
   * Get the map sorted by its values. The comparison order is defined by the
   * passed comparator object.
   *
   * @param aValueComparator
   *        The comparator to be used. May not be null.
   * @return the sorted map and never null.
   */
  @Nonnull
  @ReturnsMutableCopy
  default ICommonsOrderedMap  getSortedByValue (@Nonnull final Comparator  aValueComparator)
  {
    return CollectionHelper.getSortedByValue (this, aValueComparator);
  }

  /**
   * Get a map where keys and values are exchanged.
   *
   * @return The swapped hash map based on the type returned by
   *         {@link #createInstance()}.
   */
  @Nullable
  @ReturnsMutableCopy
  default ICommonsMap  getSwappedKeyValues ()
  {
    final ICommonsMap  ret = createInstance ();
    for (final Map.Entry  aEntry : entrySet ())
      ret.put (aEntry.getValue (), aEntry.getKey ());
    return ret;
  }

  /**
   * Special put overload that takes an entry and should simplify copying from
   * other maps.
   *
   * @param aEntry
   *        Entry to be added. May not be null.
   * @return the return value of {@link #put(Object, Object)}. May be
   *         null.
   * @since 9.0.0
   */
  @Nullable
  default VALUETYPE put (@Nonnull final Map.Entry  aEntry)
  {
    return put (aEntry.getKey (), aEntry.getValue ());
  }

  /**
   * Put the passed value into the map if the provided predicate is fulfilled.
   *
   * @param aKey
   *        Key to use. May not be null for certain map types.
   * @param aValue
   *        The value to be added. May be null in which case
   *        nothing happens.
   * @param aFilter
   *        The value predicate to be checked before insertion. May not be
   *        null.
   */
  default void putIf (@Nonnull final KEYTYPE aKey, @Nullable final VALUETYPE aValue, @Nonnull final Predicate  aFilter)
  {
    ValueEnforcer.notNull (aFilter, "Filter");
    if (aFilter.test (aValue))
      put (aKey, aValue);
  }

  /**
   * Put the passed value into the map if it is not null.
   *
   * @param aKey
   *        Key to use. May not be null for certain map types.
   * @param aValue
   *        The value to be added. May be null in which case
   *        nothing happens.
   */
  default void putIfNotNull (@Nonnull final KEYTYPE aKey, @Nullable final VALUETYPE aValue)
  {
    if (aValue != null)
      put (aKey, aValue);
  }

  /**
   * Add all passed entries to this map.
   *
   * @param aIterable
   *        Source map entries. May be null.
   * @since 8.5.5
   */
  default void putAll (@Nullable final Iterable > aIterable)
  {
    if (aIterable != null)
      for (final Map.Entry  aEntry : aIterable)
        put (aEntry.getKey (), aEntry.getValue ());
  }

  /**
   * Add all items from the passed map that match the filter to this map.
   *
   * @param aMap
   *        Source Map. May be null.
   * @param aFilter
   *        The filter to use. May be null.
   * @since 8.5.5
   */
  default void putAll (@Nullable final Map  aMap,
                       @Nullable final Predicate > aFilter)
  {
    if (aMap != null)
    {
      if (aFilter == null)
        putAll (aMap);
      else
        for (final Map.Entry  aEntry : aMap.entrySet ())
          if (aFilter.test (aEntry))
            put (aEntry.getKey (), aEntry.getValue ());
    }
  }

  /**
   * Add all items from the passed array to this map using the provided key and
   * value mapper.
   *
   * @param aElements
   *        Source collection. May be null.
   * @param aKeyMapper
   *        The key mapper. May not be null.
   * @param aValueMapper
   *        The value mapper. May not be null.
   * @param 
   *        Array element type
   * @since 8.5.5
   */
  default  void putAllMapped (@Nullable final ELEMENTTYPE [] aElements,
                                           @Nonnull final Function  aKeyMapper,
                                           @Nonnull final Function  aValueMapper)
  {
    ValueEnforcer.notNull (aKeyMapper, "KeyMapper");
    ValueEnforcer.notNull (aValueMapper, "ValueMapper");
    if (aElements != null)
      for (final ELEMENTTYPE aElement : aElements)
        put (aKeyMapper.apply (aElement), aValueMapper.apply (aElement));
  }

  /**
   * Add all items from the passed iterable to this map using the provided key
   * and value mapper.
   *
   * @param aElements
   *        Source collection. May be null.
   * @param aKeyMapper
   *        The key mapper. May not be null.
   * @param aValueMapper
   *        The value mapper. May not be null.
   * @param 
   *        Collection element type
   * @since 8.5.5
   */
  default  void putAllMapped (@Nullable final Iterable  aElements,
                                           @Nonnull final Function  aKeyMapper,
                                           @Nonnull final Function  aValueMapper)
  {
    ValueEnforcer.notNull (aKeyMapper, "KeyMapper");
    ValueEnforcer.notNull (aValueMapper, "ValueMapper");
    if (aElements != null)
      for (final ELEMENTTYPE aItem : aElements)
        put (aKeyMapper.apply (aItem), aValueMapper.apply (aItem));
  }

  /**
   * Add all items from the passed map to this map using the provided key and
   * value mapper.
   *
   * @param aMap
   *        Source map. May be null.
   * @param aKeyMapper
   *        The key mapper. May not be null.
   * @param aValueMapper
   *        The value mapper. May not be null.
   * @param 
   *        Source map key type
   * @param 
   *        Source map value type
   * @since 8.5.5
   */
  default  void putAllMapped (@Nullable final Map  aMap,
                                                        @Nonnull final Function  aKeyMapper,
                                                        @Nonnull final Function  aValueMapper)
  {
    ValueEnforcer.notNull (aKeyMapper, "KeyMapper");
    ValueEnforcer.notNull (aValueMapper, "ValueMapper");
    if (aMap != null)
      for (final Map.Entry  aEntry : aMap.entrySet ())
        put (aKeyMapper.apply (aEntry.getKey ()), aValueMapper.apply (aEntry.getValue ()));
  }

  /**
   * Add all provided values.
   *
   * @param aValues
   *        The values to be added. May be null.
   */
  default void addAll (@Nullable final Map  aValues)
  {
    if (aValues != null)
      putAll (aValues);
  }

  /**
   * Clear and add all provided values.
   *
   * @param aValues
   *        The values to be added. May be null.
   * @return {@link EChange}
   */
  @Nonnull
  default EChange setAll (@Nullable final Map  aValues)
  {
    EChange ret = removeAll ();
    if (aValues != null)
    {
      // nothing is contained
      putAll (aValues);
      if (isNotEmpty ())
        ret = EChange.CHANGED;
    }
    return ret;
  }

  /**
   * Remove all elements from this collection. This is similar to
   * {@link #clear()} but it returns a different value whether something was
   * cleared or not.
   *
   * @return {@link EChange#CHANGED} if the collection was not empty and
   *         something was removed, {@link EChange#UNCHANGED} otherwise.
   * @see #clear()
   */
  @Nonnull
  default EChange removeAll ()
  {
    if (isEmpty ())
      return EChange.UNCHANGED;
    clear ();
    return EChange.CHANGED;
  }

  /**
   * Remove the object with the passed key from this map. 
* Note: this method returns {@link EChange#UNCHANGED} even if removal was * successful if the value was null. * * @param aKey * The key to be removed. May be null. * @return {@link EChange#CHANGED} if the removal was successful, * {@link EChange#UNCHANGED} if removal fails. * @see #remove(Object) */ @Nonnull default EChange removeObject (@Nullable final KEYTYPE aKey) { return EChange.valueOf (remove (aKey) != null); } @Nonnull default EChange removeIf (@Nonnull final Predicate > aFilter) { ValueEnforcer.notNull (aFilter, "Filter"); EChange ret = EChange.UNCHANGED; final Iterator > it = entrySet ().iterator (); while (it.hasNext ()) { if (aFilter.test (it.next ())) { it.remove (); ret = EChange.CHANGED; } } return ret; } @Nonnull default EChange removeIfKey (@Nonnull final Predicate aFilter) { return removeIf (e -> aFilter.test (e.getKey ())); } @Nonnull default EChange removeIfValue (@Nonnull final Predicate aFilter) { return removeIf (e -> aFilter.test (e.getValue ())); } /** * @return An unmodifiable version of this map. Never null. * @see Collections */ @Nonnull @CodingStyleguideUnaware default Map getAsUnmodifiable () { return Collections.unmodifiableMap (this); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy