@Override
  public SmartMap<K, V> tail() {
    if (size() > 0) {
      SmartMap<K, V> resultMap = createNewInstance(internalMap);
      resultMap.remove(new SmartArrayList<K>(resultMap.keySet()).head());
      return resultMap;
    }

    throw new UnsupportedOperationException("Map is empty. No tail map available.");
  }
  @Override
  public void remove(final MapPredicate<K, V> predicate) {
    SmartMap<K, V> tempMap = createNewInstance(internalMap);
    clear();

    for (Map.Entry<K, V> entry : tempMap.entrySet()) {
      if (!predicate.test(entry.getKey(), entry.getValue())) {
        put(entry.getKey(), entry.getValue());
      }
    }
  }
  @Override
  public <S, R> SmartMap<S, R> map(
      final UnaryFunction<KeyValuePair<S, R>, java.util.Map.Entry<K, V>> function) {
    SmartMap<S, R> resultMap = createNewInstance(new HashMap<S, R>());

    for (Map.Entry<K, V> entry : internalMap.entrySet()) {
      KeyValuePair<S, R> mappedEntry = function.apply(entry);
      resultMap.put(mappedEntry.getKey(), mappedEntry.getValue());
    }

    return resultMap;
  }
  @Override
  public SmartMap<V, K> swap() {
    if (!isBijective()) {
      throw new UnsupportedOperationException("Map is not bijective!");
    }

    SmartMap<V, K> swappedMap = createNewInstance(new HashMap<V, K>());

    for (Map.Entry<K, V> entry : internalMap.entrySet()) {
      swappedMap.put(entry.getValue(), entry.getKey());
    }

    return swappedMap;
  }
  @Override
  public void mergeWith(final SmartMap<K, V> anotherMap, final BinaryFunction<V, V> mergeFunct) {
    SmartMap<K, V> resultMap = createNewInstance(internalMap);
    clear();

    for (Map.Entry<K, V> entry : anotherMap.entrySet()) {
      if (resultMap.containsKey(entry.getKey())) {
        V mergedVal = mergeFunct.apply(resultMap.get(entry.getKey()), entry.getValue());
        internalMap.put(entry.getKey(), mergedVal);
      } else {
        internalMap.put(entry.getKey(), entry.getValue());
      }
    }
  }