You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2019/08/14 22:12:00 UTC

[commons-collections] branch master updated: [COLLECTIONS-726] Add lambdas function to provide default values in MapUtils. Implementation based on the idea of PR #81. Closes #81.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-collections.git


The following commit(s) were added to refs/heads/master by this push:
     new b1511c7  [COLLECTIONS-726] Add lambdas function to provide default values in MapUtils. Implementation based on the idea of PR #81. Closes #81.
b1511c7 is described below

commit b1511c79aa749a245b48f5bf18c22e9c203882f0
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Aug 14 15:11:56 2019 -0700

    [COLLECTIONS-726] Add lambdas function to provide default values in
    MapUtils. Implementation based on the idea of PR #81. Closes #81.
    
    - The implementation is much more concise than the PR.
    - Sort methods.
    - Line length 120.
    - Close HTML tags in Javadoc.
---
 src/changes/changes.xml                            | 1961 +++++++--------
 .../org/apache/commons/collections4/MapUtils.java  | 2640 +++++++++++---------
 .../apache/commons/collections4/MapUtilsTest.java  |  126 +-
 3 files changed, 2518 insertions(+), 2209 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 69aa007..907bec1 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -1,979 +1,982 @@
-<?xml version="1.0"?>
-<!--
-     Licensed to the Apache Software Foundation (ASF) under one or more
-     contributor license agreements.  See the NOTICE file distributed with
-     this work for additional information regarding copyright ownership.
-     The ASF licenses this file to You 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.
-  -->
-<document>
-  <properties>
-    <title>Apache Commons Collections Release Notes</title>
-  </properties>
-  <body>
-  <release version="4.5" date="2019-MM-DD" description="Maintenance release.">
-    <action issue="COLLECTIONS-724" dev="ggregory" type="updated." due-to="Eitan Adler">
-      Simplify two remove-if loops #77.
-    </action>
-  </release>
-  <release version="4.4" date="2019-07-05" description="Maintenance release.">
-    <action issue="COLLECTIONS-710" dev="ggregory" type="fix" due-to="Yu Shi, Gary Gregory">
-      NullPointerExceptions in CompositeCollection, CompositeSet, and CompositeMap.
-    </action>
-    <action issue="COLLECTIONS-715" dev="ggregory" type="add" due-to="morningmemo, Gary Gregory">
-      Implement Collection's removeIf().
-    </action>
-    <action issue="COLLECTIONS-718" dev="ggregory" type="update" due-to="Eitan Adler">
-      Fix LRUMap exception message.
-    </action>
-    <action issue="COLLECTIONS-719" dev="ggregory" type="add" due-to="Gary Gregory">
-      Create a PropertiesFactory and SortedPropertiesFactory.
-    </action>
-    <action issue="COLLECTIONS-719" dev="ggregory" type="add" due-to="Stephan Windmüller, Bruno P. Kinoshita">
-      Support Transformer for LazyList #52.
-    </action>
-    <action issue="COLLECTIONS-723" dev="ggregory" type="add" due-to="Eitan Adler, SOC, Bruno P. Kinoshita">
-      Make use of FunctionalInterface #48.
-    </action>
-    <action issue="COLLECTIONS-716" dev="ggregory" type="update" due-to="Sebb">
-      Don't include email address in Exception messages.
-    </action>
-  </release>
-  <release version="4.3" date="2018-12-21" description="Update from Java 7 to Java 8, bug fixes, and small changes.">
-    <action issue="COLLECTIONS-691" dev="kinow" type="fix" due-to="Eitan Adler">
-      Use boolean operator for boolean result.
-    </action>
-    <action issue="COLLECTIONS-688" dev="ggregory" type="update">
-      Update platform requirement from Java 7 to 8.
-    </action>
-    <action issue="COLLECTIONS-689" dev="ggregory" type="update" due-to="Richard Walker">
-      Link to Javadoc API broken.
-    </action>
-    <action issue="COLLECTIONS-692" dev="ggregory" type="update" due-to="Gary Gregory, Eitan Adler">
-      Replace use of deprecated Class#newInstance() PR #49.
-    </action>
-    <action issue="COLLECTIONS-696" dev="ggregory" type="add" due-to="Maxim Solodovnik">
-      AbstractReferenceMap made easier for subclassing; PR #51.
-    </action>
-    <action issue="COLLECTIONS-701" dev="ggregory" type="fix" due-to="Shin Hong, Don Jeba">
-      StackOverflowError in SetUniqueList.add() when it receives itself.
-    </action>
-    <action issue="COLLECTIONS-703" dev="ggregory" type="fix" due-to="Tomas Tulka">
-      The PassiveExpiringMap#put() method should return the previous record only if not expired.
-    </action>
-    <action issue="COLLECTIONS-706" dev="ggregory" type="fix" due-to="Richard Eckart de Castilho, Gary Gregory">
-      Add SetUtils.unmodifiableSet(T... items) method.
-    </action>
-    <action issue="COLLECTIONS-709" dev="ggregory" type="fix" due-to="Robert Wertman">
-      MultiSet.Entry::getCount() isn't 0 after removing the last element.
-    </action>
-  </release>
-  <release version="4.2" date="2018-07-11" description="Update from Java 6 to Java 7, bug fixes, and small changes.">
-    <action issue="COLLECTIONS-681" dev="kinow" type="add" due-to="Stephan Fuhrmann">
-      Add test for MultiSetUtils
-    </action>
-    <action issue="COLLECTIONS-599" dev="ggregory" type="fix" due-to="Tejas Patel, Saleem Akbar, Gary Gregory">
-      HashEntry array object naming data initialized with double the size during deserialization.
-    </action>
-    <action issue="COLLECTIONS-662" dev="chtompki" type="fix" due-to="Vamsi Kavuri">
-      Unit tests MapUtilsTest and ListIteratorWrapperTest no longer fail on Java 9.
-    </action>
-    <action issue="COLLECTIONS-661" dev="kinow" type="fix">
-      Intermittent test failures in Windows for HashSetValuedHashMap.
-    </action>
-    <action issue="COLLECTIONS-660" dev="kinow" type="fix">
-      Uncomment test in AbstractMapTest regarding LRUMap equals.
-    </action>
-    <action issue="COLLECTIONS-658" dev="britter" type="add">
-      Add Automatic-Module-Name MANIFEST entry for Java 9 compatibility.
-    </action>
-    <action issue="COLLECTIONS-656" dev="ggregory" type="fix">
-      Fix site build on Java 8.
-    </action>
-    <action issue="COLLECTIONS-653" dev="kinow" type="fix">
-      Update Javadoc to Build on Java 1.8.
-    </action>
-    <action issue="COLLECTIONS-606" dev="chtompki" type="fix" due-to="Vamsi Kavuri">
-      Build status, Coverage status and Maven central weren't in README.md
-    </action>
-    <action issue="COLLECTIONS-602" dev="kinow" type="update" due-to="John Mark">
-      Improve efficiency of DefaultedMap.get.
-    </action>
-    <action issue="COLLECTIONS-603" dev="kinow" type="update" due-to="Artem Konovalov">
-      Small improvements for generics, conditional statements, and warnings suppressions.
-    </action>
-    <action issue="COLLECTIONS-655" dev="ggregory" type="update">
-      Update platform from Java 6 to Java 7.
-    </action>
-    <action issue="COLLECTIONS-594" dev="ggregory" type="fix" due-to="Javen O'Neal">
-      Web site spelling error: MultiValuedMapeList.
-    </action>
-    <action issue="COLLECTIONS-597" dev="ggregory" type="fix" due-to="Enrique">
-       Correction of Javadoc for org.apache.commons.collections4.functors.CatchAndRethrowClosure.
-    </action>
-    <action issue="COLLECTIONS-589" dev="ggregory" type="add" due-to="Gary Gregory">
-      Add null-safe MapUtils.size(Map&lt;?, ?>) method.
-    </action>
-    <action issue="COLLECTIONS-586" dev="ggregory" type="add" due-to="Shailender Bathula, Gary Gregory">
-      PatriciaTrie prefixMap clear throws NullPointerException.
-    </action>
-    <action issue="COLLECTIONS-654" dev="ggregory" type="add">
-      Add class SortedProperties to sort keys.
-    </action>
-    <action issue="COLLECTIONS-666" dev="ggregory" type="update" due-to="BELUGA BEHR">
-      org.apache.commons.collections4.ListUtils.union(List, List) should pre-allocate result list.
-    </action>
-    <action issue="COLLECTIONS-669" dev="ggregory" type="update" due-to="BELUGA BEHR, Gary Gregory">
-      Update org.apache.commons.collections4.CollectionUtils.addAll(Collection&lt;C>, C[]) to addAll(Collection&lt;C>, C...).
-    </action>
-    <action issue="COLLECTIONS-668" dev="ggregory" type="add" due-to="Gary Gregory">
-      Add CollectionUtils containsAny method for primitive array: org.apache.commons.collections4.CollectionUtils.containsAny(Collection&lt;?>, T...).
-    </action>
-    <action issue="COLLECTIONS-575" dev="ggregory" type="add" due-to="Guram Savinov, Grzegorz Rożniecki, Bruno P. Kinoshita, Gary Gregory">
-      Synchronized queue wrapper in QueueUtils.
-    </action>
-    <action issue="COLLECTIONS-670" dev="ggregory" type="add" due-to="Gary Gregory">
-      Add org.apache.commons.collections4.IteratorUtils.first(Iterator).
-    </action>
-    <action issue="COLLECTIONS-671" dev="ggregory" type="add" due-to="Gary Gregory">
-      Add org.apache.commons.collections4.IterableUtils.first(Iterable).
-    </action>
-    <action issue="COLLECTIONS-678" dev="ggregory" type="fix" due-to="Oscar Luis Vera Pérez">
-      The verification of unsupported iterator methods is not complete.
-    </action>
-    <action issue="COLLECTIONS-673" dev="ggregory" type="fix" due-to="John Mark, Stephan Fuhrmann">
-      ListUtils.partition potential integer overflow.
-    </action>
-  </release>
-  <release version="4.1" date="2015-11-28" description="This is a security and minor release.">
-    <action issue="COLLECTIONS-508" dev="tn" type="add">
-      Added new interfaces "MultiValuedMap", "ListValuedMap" and "SetValuedMap"
-      as a replacement for "MultiMap". Decorators and implementations reside in
-      the "multimap" package and a "MultiMapUtils" class has been added.
-      The existing interface "MultiMap" as well as the concrete implementation
-      "MultiValueMap" has been deprecated.
-    </action>
-    <action issue="COLLECTIONS-551" dev="tn" type="update">
-      Deprecated various method in "CollectionUtils" in favor of similar
-      methods in the newly introduced "IterableUtils".
-    </action>
-    <action issue="COLLECTIONS-580" dev="tn" type="update">
-      Serialization support for unsafe classes in the functor package
-      has been removed as this can be exploited for remote code execution
-      attacks. Classes considered to be unsafe are: CloneTransformer,
-      ForClosure, InstantiateFactory, InstantiateTransformer, InvokerTransformer,
-      PrototypeCloneFactory, PrototypeSerializationFactory, WhileClosure.
-    </action>
-    <action issue="COLLECTIONS-580" dev="tn" type="fix">
-      Added validation when de-serializing a "MultiValueMap#ReflectionFactory":
-      only Collection classes are allowed, otherwise an UnsupportedOperationException
-      will be thrown during de-serialization.
-    </action>
-    <action issue="COLLECTIONS-567" dev="tn" type="add">
-      Added new MultiSet interface which is intended to be a replacement for
-      the Bag interface. The main difference is that a MultiSet is fully compatible
-      to the Collection contract.
-    </action>
-    <action issue="COLLECTIONS-576" dev="tn" type="fix" due-to="Stephan Roch">
-      Subclasses of MultiKey did not re-calculate their hashcode after de-serialization.
-    </action>
-    <action issue="COLLECTIONS-572" dev="tn" type="add">
-      Added set operations to "SetUtils": union, difference, intersection and disjunction.
-      The operations return a view of the result that is backed by the input sets.
-    </action>
-    <action issue="COLLECTIONS-570" dev="tn" type="update">
-      All constructors and static factory methods will now throw a "NullPointerException" if
-      a required input argument is null. Previously sometimes a "IllegalArgumentException" was used.
-    </action>
-    <action issue="COLLECTIONS-571" dev="tn" type="update">
-      Deprecated methods "synchronizedCollection(Collection)" and "unmodifiableCollection(Collection)"
-      in class "CollectionUtils", the corresponding methods in "java.util.Collections" should be used instead.
-    </action>
-    <action issue="COLLECTIONS-566" dev="tn" type="fix">
-      "IteratorUtils#collate(...)" methods did not use natural ordering when a
-      null comparator was provided.
-    </action>
-    <action issue="COLLECTIONS-557" dev="tn" type="add" due-to="Philippe Mouawad">
-      Added support to specify the initial size of a "LRUMap".
-    </action>
-    <action issue="COLLECTIONS-565" dev="tn" type="add">
-      Added decorators for "NavigableSet" interface.
-    </action>
-    <action issue="COLLECTIONS-464,COLLECTIONS-442" dev="tn" type="add">
-      Added new class "FluentIterable" to support a fluent API for manipulating
-      Iterable instances. Additionally various supporting methods have been
-      added to "IterableUtils" and "IteratorUtils".
-    </action>
-    <action issue="COLLECTIONS-464" dev="tn" type="add">
-      Added new "ZippingIterator" and factory methods "IteratorUtils#zippingIterator(...)".
-    </action>
-    <action issue="COLLECTIONS-464" dev="tn" type="add">
-      Added new decorator "SkippingIterator" and factory methods "IteratorUtils#skippingIterator(...)".
-    </action>
-    <action issue="COLLECTIONS-556" dev="tn" type="add">
-      Added method "SetUtils#newIdentityHashSet()" which returns a new identity HashSet
-      using reference-equality instead of object-equality.
-    </action>
-    <action issue="COLLECTIONS-562" dev="tn" type="update">
-      Upgraded minimum java requirement to Java 6 (up from Java 5).
-    </action>
-    <action issue="COLLECTIONS-395" dev="tn" type="add" due-to="David Hawthorne">
-      Added method "LRUMap#get(Object, boolean)" that allows to query the map
-      without affecting the least recently used order.
-    </action>
-    <action issue="COLLECTIONS-558" dev="tn" type="fix" due-to="Felix Rabe">
-      Changed return type of "ListOrderedSet#remove(int)" from Object to the generic type parameter.
-    </action>
-    <action issue="COLLECTIONS-555" dev="tn" type="fix" due-to="M Kim">
-      Added clarification to javadoc of "TreeBag#add(Object)" wrt null arguments.
-    </action>
-    <action issue="COLLECTIONS-427" dev="tn" type="add" due-to="Gonçalo Marques">
-      Added "toString(...)" methods to newly created "IterableUtils" and existing "IteratorUtils"
-      to get a string representation of an Iterable/Iterator instance similar to "Arrays#toString(...)".
-    </action>
-    <action issue="COLLECTIONS-427" dev="tn" type="fix">
-      Reverted performance improvement for "SetUniqueList#retainAll(Collection)"
-      introduced in 4.0. Added clarifying javadoc wrt runtime complexity instead.
-    </action>
-    <action issue="COLLECTIONS-426" dev="tn" type="fix">
-      Reverted performance improvement for "ListOrderedSet#retainAll(Collection)"
-      introduced in 4.0. Added clarifying javadoc wrt runtime complexity instead.
-    </action>
-    <action issue="COLLECTIONS-530" dev="tn" type="fix" due-to="Erik">
-      Added a Builder for "PredicatedCollection". Elements added to the builder
-      that fail the predicate will not throw an IllegalArgumentException. The builder
-      supports creating predicated lists, bags, sets and queues.
-    </action>
-    <action issue="COLLECTIONS-545" dev="tn" type="fix" due-to="Oswaldo Olivo">
-      Documented runtime complexity of "CollectionUtils#removeAll(Collection, Collection).
-    </action>
-    <action issue="COLLECTIONS-543" dev="tn" type="fix">
-      "AbstractCollectionDecorator" doesn't forward equals and hashCode anymore.
-    </action>
-    <action issue="COLLECTIONS-544" dev="tn" type="fix" due-to="Oswaldo Olivo">
-      Documented runtime complexity of "CollectionUtils#retainAll(Collection, Collection).
-    </action>
-    <action issue="COLLECTIONS-542" dev="tn" type="fix">
-      "AbstractHashedMap" still inherits from "AbstractMap", contrary to what
-      the class javadoc stated. The inheritance will now be removed in v5.0.
-    </action>
-    <action issue="COLLECTIONS-539" dev="tn" type="add" due-to="Guram Savinov">
-      Changed scope of "CircularFifoQueue#isAtFullCapacity()" to public.
-    </action>
-    <action issue="COLLECTIONS-525" dev="tn" type="fix" due-to="Zigler Zhang">
-      The map returned by "PatriciaTrie#prefixMap()" did not contain all keys
-      that are prefixed by the given search key in some rare cases.
-    </action>
-    <action issue="COLLECTIONS-511" dev="tn" type="add" due-to="Nathan Blomquist, Brent Worden">
-      Added new methods "IterableUtils#partition(...)" to partition an input collection
-      into separate output collections based on evaluation of one or more predicates.
-    </action>
-    <action issue="COLLECTIONS-537" dev="tn" type="fix" due-to="Frank Jakop">
-      Harmonized signature of factory methods for functor-related classes which take
-      a collection as input with their array counterparts.
-    </action>
-    <action issue="COLLECTIONS-540" dev="tn" type="fix" due-to="Daniel Stewart, Issam El Atif">
-      Added overloaded method "CollectionUtils#get(Enumeration, int)" and simplified
-      code for "CollectionUtils#get(Object, int)".
-    </action>
-    <action issue="COLLECTIONS-536" dev="tn" type="fix" due-to="Tagir Valeev">
-      Improved check for null input in "MapUtils#putAll(Map, Object[])".
-    </action>
-    <action issue="COLLECTIONS-534" dev="tn" type="fix" due-to="Oswaldo Olivo">
-      Added clarifying javadoc wrt runtime complexity of "CollectionBag#retainAll".
-    </action>
-    <action issue="COLLECTIONS-529" dev="tn" type="add" due-to="Alexander Muthmann, Dipanjan Laha">
-      Added methods "removeAll(...)" and "retainAll(...)" to "CollectionUtils" that perform
-      equality checks using the provided "Equator" object instead of "Object#equals()".
-    </action>
-    <action issue="COLLECTIONS-531" dev="tn" type="fix" due-to="Dipanjan Laha">
-      Use correct type bounds in
-      "CollectionUtils#isEqualCollection(Collection, Collection, Equator)" to
-      prevent a "ClassCastException" at runtime for invalid inputs.
-    </action>
-    <action issue="COLLECTIONS-523" dev="tn" type="fix" due-to="Thiago Andrade">
-      Removed unneeded private method in "PassiveExpiringMap".
-    </action>
-    <action issue="COLLECTIONS-516" dev="tn" type="fix" due-to="Cyrille Artho">
-      Added clarification to the javadoc of "MapUtils#toProperties(Map)" in case
-      of null keys/values.
-    </action>
-    <action issue="COLLECTIONS-524" dev="tn" type="fix" due-to="J Goodfellow">
-      "ListOrderedSet#listOrderedSet(List)" did not remove duplicates from the
-      input list as advertised in the javadoc.
-    </action>
-    <action issue="COLLECTIONS-521" dev="tn" type="fix" due-to="Maxime Nay">
-      "MultiKeyMap" was throwing a "NullPointerException" for various operations
-      if two key arguments have been used and the second was "null".
-    </action>
-    <action issue="COLLECTIONS-522" dev="tn" type="fix" due-to="Erik">
-      Updated code example for "PredicatedList".
-    </action>
-    <action issue="COLLECTIONS-512" dev="tn" type="fix" due-to="Cyrille Artho">
-      "TransformingComparator" and "FixedOrderComparator" did not comply with
-      the contract of "Object#equals".
-    </action>
-    <action issue="COLLECTIONS-510" dev="tn" type="fix" due-to="Hollis Waite">
-      Fix compilation errors when using source level 1.8 and a recent java 8 compiler.
-    </action>
-    <action issue="COLLECTIONS-509" dev="tn" type="fix">
-      Clarified javadoc of "CollectionBag" wrt changes from the original Bag interface.
-    </action>
-    <action issue="COLLECTIONS-507" dev="tn" type="fix" due-to="Gerson">
-      Removed wrong type bounds for "ComparatorUtils#chainedComparator(...)".
-    </action>
-    <action issue="COLLECTIONS-506" dev="tn" type="fix" due-to="Anthony Communier">
-      Added javadoc clarification to class "CollectionUtils" that input objects which
-      override "Object#equals(Object)" must also maintain the general contract of
-      "Object#hashCode()" as various utility methods take advantage of sets/maps/bags.
-    </action>
-    <action issue="COLLECTIONS-503" dev="tn" type="add" due-to="Josh Cain">
-      Added new transformer "IfTransformer" and factory methods "TransformerUtils#ifTransformer(...)"
-      which replace "TransformerUtils#switchTransformer(Predicate, Transformer, Transformer)".
-    </action>
-    <action issue="COLLECTIONS-471" dev="tn" type="add" due-to="Radford Tam">
-      Added new decorator "BoundedIterator" and factory methods "IteratorUtils#boundedIterator(...)".
-    </action>
-  </release>
-  <release version="4.0" date="2013-11-27" description="
-This is a major release: It combines bug fixes, new features and
-changes to existing features.
-
-Most notable changes are: use of generics and other language features introduced in Java 5 (varargs, Iterable),
-removed deprecated classes / methods and features which are now supported by the JDK,
-replaced Buffer interface with java.util.Queue,
-added concept of split maps with respective interfaces Put / Get (see also package splitmap),
-added new Trie interface together with an implementation of a Patricia Trie.
-
-Because of the base package name change, this release can be used together
-with earlier versions of Commons Collections.
-The minimal version of the Java platform required to compile and use
-Commons Collections is Java 5.
-Users are encouraged to upgrade to this version as, in addition to new
-features, this release includes numerous bug fixes.
-  ">
-    <action issue="COLLECTIONS-502" dev="tn" type="update">
-      Resolved generic parameter inconsistency for various static fields, e.g. BagUtils.EMPTY_BAG,
-      TruePredicate.INSTANCE and many others. All accessible static fields use raw types so that
-      they can be used directly without explicit casting. To avoid compiler warnings about unchecked
-      conversion and/or rawtypes use the corresponding factory methods, e.g. BagUtils.emptyBag().
-    </action>
-    <action issue="COLLECTIONS-501" dev="tn" type="update">
-      Renamed methods "V MultiKeyMap#remove(Object, Object, ...)" to
-      "V MultiKeyMap#removeMultiKey(Object, Object, ...)" to avoid future conflicts
-      with a default method of the Map interface in Java 8.
-    </action>
-    <action issue="COLLECTIONS-500" dev="tn" type="update">
-      Renamed "V MultiMap#remove(K, V)" to "boolean MultiMap#removeMapping(K, V)"
-      to avoid future conflicts with a default method of the Map interface in Java 8.
-    </action>
-    <action issue="COLLECTIONS-499" dev="tn" type="update">
-      Refactored the test framework for Bag implementations to extend from
-      "AbstractCollectionTest" by decorating the concrete Bag instance with
-      a CollectionBag or CollectionSortedBag.
-    </action>
-    <action issue="COLLECTIONS-498" dev="tn" type="fix">
-      "CollectionBag" will now also respect the contract of the decorated bag in case
-      a null argument is provided to either removeAll or retainAll.
-    </action>
-    <action issue="COLLECTIONS-497" dev="tn" type="add">
-      Added bag decorator "CollectionSortedBag" which decorates a SortedBag to make it
-      comply with the Collection contract.
-    </action>
-    <action issue="COLLECTIONS-496" dev="tn" type="update">
-      "UnmodifiableBoundedCollection" does now also implement the marker interface "Unmodifiable"
-      similar as all other unmodifiable decorators.
-    </action>
-    <action issue="COLLECTIONS-495" dev="tn" type="fix">
-      "UnmodifiableTrie#unmodifiableTrie(Trie)" will not decorate again an already
-      unmodifiable Trie. Also the return type has been changed to "Trie" to be consistent
-      with other Unmodifiable decorators.
-    </action>
-    <action issue="COLLECTIONS-494" dev="tn" type="update" due-to="Emmanuel Bourg">
-      Moved "Equator" interface to base package for consistency.
-    </action>
-    <action issue="COLLECTIONS-488" dev="tn" type="add" due-to="Josh Cain">
-      Added "CollectionsUtils#matchesAll(Iterable, Predicate)" to test if all elements
-      of a collection match a given predicate.
-    </action>
-    <action issue="COLLECTIONS-485" dev="tn" type="fix" due-to="Hollis Waite">
-      Accept wildcard input where possible, e.g. in copy-constructors, Unmodifiable* decorators
-      and iterators.
-    </action>
-    <action issue="COLLECTIONS-481" dev="tn" type="fix" due-to="Hollis Waite">
-      No collision detection/resolution was performed when calling "CompositeSet#addComposited(...)"
-      with more than one Set as argument. Additionally use varargs parameters instead of arrays
-      in CompositeSet and CompositeCollection constructor and addComposited method.
-    </action>
-    <action issue="COLLECTIONS-480" dev="tn" type="update" due-to="Hollis Waite">
-      Narrow return type of "BidiMap#values()" to Set as the values are required to be unique.
-    </action>
-    <action issue="COLLECTIONS-475" dev="tn" type="fix">
-      Fixed conversion of timeout parameters in "PassiveExpiringMap".
-    </action>
-    <action issue="COLLECTIONS-474" dev="sebb" type="fix" due-to="Ning Chen">
-      Exception in "ListOrderedMap#putAll" if map contains null values.
-    </action>
-    <action issue="COLLECTIONS-473" dev="tn" type="update" due-to="sebb">
-      Made field "collection" in class "AbstractCollectionDecorator" private and added
-      setter "setCollection(Collection)" with scope protected to set the decorated collection
-      during de-serialization.
-    </action>
-    <action issue="COLLECTIONS-472" dev="tn" type="fix" due-to="Adrian Nistor">
-      Improved performance of "AbstractMapBag#containsAll(Collection)" by returning immediately
-      after a difference has been found.
-    </action>
-    <action issue="COLLECTIONS-470" dev="tn" type="update" due-to="sebb">
-      Renamed class "TransformedMap" in package "splitmap" to "TransformedSplitMap" to avoid
-      name clash with similar class in package "map".
-    </action>
-    <action issue="COLLECTIONS-468" dev="tn" type="add">
-      Added bag decorator "CollectionBag" which decorates a bag to make it comply with the
-      Collection contract.
-    </action>
-    <action issue="COLLECTIONS-466" dev="tn" type="update">
-      Replaced "Collection" with "Iterable" for method arguments where applicable.
-    </action>
-    <action issue="COLLECTIONS-463" dev="tn" type="add" due-to="Andy Seaborne, Claude Warren">
-      Added "PushbackIterator" decorator to support pushback of elements during iteration.
-    </action>
-    <action issue="COLLECTIONS-462" dev="tn" type="add" due-to="Andy Seaborne, Claude Warren">
-      Added "PeekingIterator" decorator to support one-element lookahead during iteration.
-    </action>
-    <action issue="COLLECTIONS-461" dev="tn" type="fix" due-to="Matt Benson, sebb">
-      Added additional clarification to javadoc of interface "Put" wrt return type of
-      "put(Object, Object)" method.
-    </action>
-    <action issue="COLLECTIONS-460" dev="tn" type="update">
-      Changed "IteratorChain" to use internally a "Queue" instead of a "List". Iterators are
-      removed from the queue once used and can be garbage collected after being exhausted.
-      Additionally removed the methods "setIterator(Iterator)" and "getIterators()".
-    </action>
-    <action issue="COLLECTIONS-459" dev="tn" type="update" due-to="sebb">
-      Removed method "setArray(Object)" in class ArrayIterator and method "setArray(Object[])"
-      in class ObjectArrayIterator and made fields array, startIndex and endIndex final.
-    </action>
-    <action issue="COLLECTIONS-458" dev="sebb" type="remove">
-      Removed unused class "AbstractUntypedCollectionDecorator&lt;E, D&gt;".
-    </action>
-    <action issue="COLLECTIONS-456" dev="tn" type="add">
-      Added methods "ListUtils#longestCommonSubsequence(...)" to get the longest common subsequence
-      of arbitrary lists or CharSequences.
-    </action>
-    <action issue="COLLECTIONS-455" dev="sebb" type="update">
-      Changed scope of fields to private where appropriate.
-    </action>
-    <action issue="COLLECTIONS-454" dev="tn" type="update">
-      An iterator over a "Flat3Map#entrySet()" will now return
-      independent Map.Entry objects that will not change anymore when
-      the iterator progresses.
-    </action>
-    <action issue="COLLECTIONS-453" dev="tn" type="update">
-      Several closure and transformer implementations in the functors package
-      will now copy an array as input to their constructor (e.g. ChainedClosure).
-    </action>
-    <action issue="COLLECTIONS-452" dev="tn" type="update">
-      Change base package to org.apache.commons.collections4.
-    </action>
-    <action issue="COLLECTIONS-451" dev="tn" type="update">
-      The constructors for all Utils classes are now private to prevent instantiation.
-    </action>
-    <action issue="COLLECTIONS-450" dev="tn" type="add" due-to="J. Moldawski">
-      Added methods "forAllButLastDo(Collection, Closure)" and "forAllButLastDo(Iterator, Closure)"
-      to class "CollectionUtils".
-    </action>
-    <action issue="COLLECTIONS-447" dev="tn" type="fix" due-to="Jeffrey Barnes">
-      Tree traversal with a TreeListIterator will not be affected anymore by
-      the removal of an element directly after a call to previous().
-    </action>
-    <action issue="COLLECTIONS-446" dev="tn" type="add" due-to="Matt Lachman">
-      Added method "CollectionUtils#isEqualCollection(Collection, Collection, Equator)".
-    </action>
-    <action issue="COLLECTIONS-445" dev="tn" type="fix">
-      Adapt and/or ignore several unit tests when run on a IBM J9 VM (specification version 1.6.0)
-      due to a faulty "java.util.TreeMap" implementation.
-    </action>
-    <action issue="COLLECTIONS-444" dev="tn" type="fix" due-to="Thomas Vahrst, John Vasileff">
-      SetUniqueList.set(int, E) now works correctly if the object to be inserted
-      is already placed at the given position.
-    </action>
-    <action issue="COLLECTIONS-441" dev="tn" type="fix" due-to="Thomas Vahrst">
-      MultiKeyMap.clone() now correctly calls super.clone().
-    </action>
-    <action issue="COLLECTIONS-436" dev="tn" type="add" due-to="Arman Sharif">
-      Added "emptyIfNull" methods to classes "CollectionUtils", "ListUtils", "SetUtils"
-      and "MapUtils".
-    </action>
-    <action issue="COLLECTIONS-433" dev="tn" type="fix" due-to="Jeffrey Barnes">
-      Improve performance of "TreeList#addAll" and "TreeList(Collection)" by converting
-      the input collection into an AVL tree and efficiently merge it into the existing tree.
-    </action>
-    <action issue="COLLECTIONS-432" dev="tn" type="update">
-      Replaced "Buffer" interface with "java.util.Queue". Kept "CircularFifoQueue"
-      as well as "Predicated", "Transformed" and "Unmodifiable" decorators.
-    </action>
-    <action issue="COLLECTIONS-429,COLLECTIONS-434" dev="tn" type="add" due-to="Adrian Nistor, Mert Guldur">
-      Added method "CollectionUtils#containsAll(Collection, Collection)" with guaranteed
-      runtime complexity of O(n + m) and space complexity of O(n). This method may yield much
-      better performance than "Collection#containsAll(Collection)" depending on the use-case and
-      type of collection used.
-    </action>
-    <action issue="COLLECTIONS-427" dev="brentworden" type="fix" due-to="Mert Guldur">
-      Fixed performance issue in "SetUniqueList#retainAll" method for large collections.
-    </action>
-    <action issue="COLLECTIONS-426" dev="brentworden" type="fix" due-to="Adrian Nistor">
-      Fixed performance issue in "ListOrderedSet#retainAll" method for large collections.
-    </action>
-    <action issue="COLLECTIONS-425" dev="tn" type="fix" due-to="Adrian Nistor">
-      Improved performance of "ListOrderedMap#remove(Object)" method.
-    </action>
-    <action issue="COLLECTIONS-424" dev="tn" type="update" due-to="Michael Pradel">
-      "CompositeSet" does not inherit from "CompositeCollection" anymore. The inner class
-      "SetMutator" has been updated accordingly.
-    </action>
-    <action issue="COLLECTIONS-422" dev="tn" type="add" due-to="Benoit Corne">
-      Added method "CollectionUtils#permutations(Collection)" and class "PermutationIterator"
-      to generate unordered permutations of a collection.
-    </action>
-    <action issue="COLLECTIONS-421" dev="tn" type="fix" due-to="Benedikt Ritter">
-      Update javadoc for "ListUtils#lazyList()" and "ListUtils#fixedSizeList()".
-    </action>
-    <action issue="COLLECTIONS-419" dev="tn" type="fix" due-to="Adrian Nistor">
-      Added clarifying javadoc wrt runtime complexity of "AbstractDualBidiMap#retainAll".
-    </action>
-    <action issue="COLLECTIONS-417" dev="tn" type="fix" due-to="Adrian Nistor">
-      Added clarifying javadoc wrt runtime complexity of "AbstractLinkedList#retainAll".
-    </action>
-    <action issue="COLLECTIONS-415" dev="tn" type="fix" due-to="Adrian Nistor">
-      Added clarifying javadoc wrt runtime complexity of "AbstractLinkedList#removeAll".
-    </action>
-    <action issue="COLLECTIONS-414" dev="tn" type="fix">
-      Fixed several compilation issues with older Java 1.6 compilers.
-    </action>
-    <action issue="COLLECTIONS-413" dev="tn" type="fix" due-to="Adrian Nistor">
-      Improved performance of "removeAll()" method for sets returned by "DualHashBidiMap#entrySet()".
-    </action>
-    <action issue="COLLECTIONS-412" dev="tn" type="fix" due-to="Adrian Nistor">
-      Improved performance of "CollectionUtils#subtract" methods.
-    </action>
-    <action issue="COLLECTIONS-411" dev="tn" type="fix" due-to="Adrian Nistor">
-      Fixed possible "IndexOutOfBoundsException" in "ListOrderedMap#putAll".
-    </action>
-    <action issue="COLLECTIONS-410" dev="tn" type="fix" due-to="Adrian Nistor">
-      Improved performance of "SetUniqueList#addAll" method.
-    </action>
-    <action issue="COLLECTIONS-409" dev="tn" type="fix" due-to="Adrian Nistor">
-      Improved performance of "ListOrderedSet#addAll" method.
-    </action>
-    <action issue="COLLECTIONS-408" dev="tn" type="fix" due-to="Adrian Nistor">
-      Improved performance of "SetUniqueList#removeAll".
-    </action>
-    <action issue="COLLECTIONS-407" dev="tn" type="fix" due-to="Adrian Nistor">
-      Improved performance of "ListOrderedSet#remove(Object)" in case the object is
-      not contained in the Set.
-    </action>
-    <action issue="COLLECTIONS-406" dev="tn" type="fix" due-to="Adrian Nistor">
-      Improved performance of "ListUtils#subtract" method.
-    </action>
-    <action issue="COLLECTIONS-405" dev="brentworden" type="add" due-to="Adam Dyga">
-      Added "ListUtils#select" and "ListUtils#selectRejected" methods.
-    </action>
-    <action issue="COLLECTIONS-404" dev="luc" type="add" due-to="Jordane Sarda">
-      Added an implementation of Eugene Myers difference algorithm in package
-      o.a.c.c.sequence.
-    </action>
-    <action issue="COLLECTIONS-400" dev="tn" type="fix" due-to="Shin Hwei Tan">
-      Added missing null check in "CollectionUtils#addIgnoreNull(Collection, Object)".
-    </action>
-    <action issue="COLLECTIONS-399" dev="tn" type="add" due-to="Sebb">
-      Added new method "get(int)" to "CircularFifoQueue".
-    </action>
-    <action issue="COLLECTIONS-396" dev="tn" type="add" due-to="Jeff Rodriguez">
-      Added "LazyIteratorChain" iterator.
-    </action>
-    <action issue="COLLECTIONS-393" dev="tn" type="add" due-to="Chris Shayan">
-      Added "ListUtils#partition" method to split a List into consecutive sublists.
-    </action>
-    <action issue="COLLECTIONS-391" dev="tn" type="fix" due-to="Shin Hwei Tan">
-      Fixed javadoc for "MapUtils#toProperties(Map)".
-    </action>
-    <action issue="COLLECTIONS-389" dev="tn" type="fix" due-to="Shin Hwei Tan">
-      Clarified javadoc for "TransformerUtils#mapTransformer" for null input.
-    </action>
-    <action issue="COLLECTIONS-388" dev="tn" type="fix" due-to="Shin Hwei Tan">
-      Clarified javadoc for "FactoryUtils#prototypeFactory" for null input.
-    </action>
-    <action issue="COLLECTIONS-384" dev="ggregory" type="fix" due-to="Shin Hwei Tan">
-      Fixed inconsistent javadoc for "MapUtils#synchronizedMap(Map)".
-    </action>
-    <action issue="COLLECTIONS-383" dev="tn" type="add" due-to="Adrian Cumiskey">
-      Added "CollectionUtils#forAllDo" implementation which takes an "Iterator" as input.
-    </action>
-    <action issue="COLLECTIONS-382" dev="tn" type="update" due-to="Olivier Lamy">
-      Change maven coordinates to "org.apache.commons.commons-collections4".
-    </action>
-    <action issue="COLLECTIONS-381" dev="sebb" type="update" due-to="Olivier Lamy">
-      Move the project structure to a standard maven layout.
-    </action>
-    <action issue="COLLECTIONS-380" dev="tn" type="fix" due-to="Dave Brosius">
-      Fixed infinite loop when calling "UnmodifiableBoundedCollection#unmodifiableBoundedCollection()".
-    </action>
-    <action issue="COLLECTIONS-379" dev="tn" type="fix" due-to="Shin Hwei Tan">
-      Fixed javadoc for several methods wrt expected NullPointerExceptions.
-    </action>
-    <action issue="COLLECTIONS-375" dev="tn" type="add" due-to="Ivan Hristov">
-      Added method "ListUtils#defaultIfNull(List, List)".
-    </action>
-    <action issue="COLLECTIONS-372" dev="tn" type="update">
-      TransformingComparator now supports different types for its input/output values.
-    </action>
-    <action issue="COLLECTIONS-364" dev="sebb" type="fix">
-      "DualTreeBidiMap" now uses the correct comparator for the reverse map during de-serialization.
-    </action>
-    <action issue="COLLECTIONS-363" dev="sebb" type="fix">
-      "TransformedMap" in the package "splitmap" can now be serialized.
-    </action>
-    <action issue="COLLECTIONS-362" dev="brentworden" type="update" due-to="Jean-Noel Rouvignac">
-      "CollectionUtils#filter(Iterable, Predicate)" will now return whether the collection
-      has been modified.
-    </action>
-    <action issue="COLLECTIONS-361" dev="tn" type="add" due-to="Jean-Noel Rouvignac">
-      Add method "CollectionUtils#filterInverse(Iterable, Predicate)".
-    </action>
-    <action issue="COLLECTIONS-360" dev="jochen" type="fix" due-to="Sai Zhang">
-      "FilterListIterator#hasNext" does not throw a NullPointerException anymore
-      to comply to the Java iterator specification.
-    </action>
-    <action issue="COLLECTIONS-359" dev="bayard" type="fix" due-to="Mark Shead">
-      "ListUtils#intersection(List, List)" will now also work correctly if there
-      are duplicate elements in the provided lists.
-    </action>
-    <action issue="COLLECTIONS-352" dev="bayard" type="fix" due-to="Adam Gent">
-      "AbstractCollectionDecorator" will now use internally "decorated()" to access
-      the decorated collection.
-    </action>
-    <action issue="COLLECTIONS-351" dev="bayard" type="remove" due-to="Henri Yandell">
-      Removed features which are now supported by the JDK.
-    </action>
-    <action issue="COLLECTIONS-350" dev="bayard" type="fix" due-to="Michael Akerman">
-      Removed debug output in "MapUtils#getNumber(Map)".
-    </action>
-    <action issue="COLLECTIONS-348" dev="brentworden" type="fix" due-to="Paul Benedict">
-      Fixed javadoc for all "transformedXXX(XXX)" methods in the respective Utils classes
-      to clarify that existing objects in the list are not transformed.
-    </action>
-    <action issue="COLLECTIONS-343" dev="mbenson" type="fix" due-to="Goran Hacek">
-      Singleton classes in package "functors" are now correctly de-serialized.
-    </action>
-    <action issue="COLLECTIONS-341" dev="mbenson" type="update" due-to="Goran Hacek">
-      "NOPClosure" is now a final class.
-    </action>
-    <action issue="COLLECTIONS-340" dev="mbenson" type="fix" due-to="Goran Hacek">
-      Removed broken methods "equals(Object)" and "hashCode()" in class "NOPClosure".
-    </action>
-    <action issue="COLLECTIONS-336" dev="bayard" type="fix" due-to="sebb">
-      Simplified exceptions as the cause is available from the parent.
-    </action>
-    <action issue="COLLECTIONS-335" dev="jochen" type="fix" due-to="sebb">
-      Fixed cache assignment for "TreeBidiMap#entrySet".
-    </action>
-    <action issue="COLLECTIONS-334" dev="jochen" type="fix" due-to="sebb">
-      Synchronized access to lock in "StaticBucketMap#size()".
-    </action>
-    <action issue="COLLECTIONS-332" dev="jochen" type="fix" due-to="Tom Parker">
-      Added clarification to javadoc of "ListOrderedMap" that "IdentityMap" and
-      "CaseInsensitiveMap" are not supported.
-    </action>
-    <action issue="COLLECTIONS-331" dev="jochen" type="fix" due-to="Michael Krkoska">
-      Improve javadoc of "CollatingIterator" wrt the used "Comparator" and throw a
-      NullPointerException in "CollatingIterator#least" if no comparator is set.
-    </action>
-    <action issue="COLLECTIONS-330" dev="mbenson" type="fix" due-to="Joerg Schaible">
-      "LRUMap#keySet()#remove(Object)" will not throw a "ConcurrentModificationException" anymore.
-    </action>
-    <action issue="COLLECTIONS-328" dev="bayard" type="fix" due-to="Thomas Rogan, Jilles van Gurp">
-      Improved performance of "ListUtils#intersection(List, List)".
-    </action>
-    <action issue="COLLECTIONS-327" dev="brentworden" type="add" due-to="sebb">
-      Added serialVersionUID fields for "CompositeCollection", "CompositeSet",
-      "EmptyMapMutator", "EmptySetMutator".
-    </action>
-    <action issue="COLLECTIONS-324" dev="tn" type="update" due-to="sebb">
-      Fields transformer and decorated in class "TransformingComparator" are now final.
-    </action>
-    <action issue="COLLECTIONS-323" dev="jochen" type="fix" due-to="Maarten Brak">
-      Changed behavior of "CaseInsensitiveMap" constructor to be compliant with "HashMap"
-      in case the initial capacity is set to zero.
-    </action>
-    <action issue="COLLECTIONS-322" dev="tn" type="add" due-to="Thomas Vahrst">
-      Added NodeListIterator and convenience methods in IteratorUtils to iterate over
-      a org.w3c.dom.NodeList.
-    </action>
-    <action issue="COLLECTIONS-320" dev="bayard" type="fix" due-to="sebb">
-      Improved performance of "StaticBucketMap#putAll(Map)" by iterating over the entry set.
-    </action>
-    <action issue="COLLECTIONS-319" dev="bayard" type="fix" due-to="sebb">
-      Avoid redundant null check in "IteratorUtils#getIterator(Object)".
-    </action>
-    <action issue="COLLECTIONS-317" dev="bayard" type="fix" due-to="sebb">
-      Use a private method to populate the object in "AbstractHashedMap(Map)".
-    </action>
-    <action issue="COLLECTIONS-316" dev="bayard" type="fix" due-to="ori">
-      Fixed javadoc of "LRUMap" wrt to the maxSize parameter of the constructor.
-    </action>
-    <action issue="COLLECTIONS-313" dev="brentworden" type="add" due-to="David J. M. Karlsen">
-      Added new abstract class "CatchAndRethrowClosure" that re-throws any checked exception
-      as unchecked "FunctorException".
-    </action>
-    <action issue="COLLECTIONS-312" dev="tn" type="fix" due-to="Peter Lawrey, Gary Gregory">
-      Use of final keyword where applicable, minor performance improvements by properly
-      initializing the capacity of newly created collections when known in advance.
-    </action>
-    <action issue="COLLECTIONS-307" dev="tn" type="update" due-to="Christian Semrau, Thomas Vahrst">
-      "SetUniqueList#subList()" will now return an unmodifiable list as changes to it
-      may invalidate the parent list.
-    </action>
-    <action issue="COLLECTIONS-307" dev="bayard" type="fix" due-to="Christian Semrau">
-      "SetUniqueList#subList()#contains(Object)" will now correctly check the subList
-      rather than the parent list.
-    </action>
-    <action issue="COLLECTIONS-306" dev="brentworden" type="add" due-to="Chris Shayan">
-      Added method "CollectionUtils#subtract(Iterable, Iterable, Predicate)".
-    </action>
-    <action issue="COLLECTIONS-304" dev="bayard" type="fix" due-to="Rafał Figas,Bjorn Townsend">
-      "SetUniqueList#set(int, Object)" will now correctly enforce the uniqueness constraint.
-    </action>
-    <action issue="COLLECTIONS-303" dev="bayard" type="fix" due-to="Emmanuel Bourg">
-      Improved javadoc for "Unmodifiable*" classes wrt behavior when the users tries
-      to modify the collection.
-    </action>
-    <action issue="COLLECTIONS-298" dev="bayard" type="update" due-to="Benjamin Bentmann">
-      Calling "CollectionUtils#sizeIsEmpty(null)" will now return true.
-    </action>
-    <action issue="COLLECTIONS-296" dev="tn" type="add" due-to="Julius Davies">
-      Added methods "CollectionUtils#collate(...)" to merge two sorted Collections
-      into a sorted List using the standard O(n) merge algorithm.
-    </action>
-    <action issue="COLLECTIONS-294" dev="bayard" type="fix" due-to="Benjamin Bentmann">
-      "CaseInsensitiveMap" will now convert input strings to lower-case in a
-      locale-independant manner.
-    </action>
-    <action issue="COLLECTIONS-293" dev="tn" type="add" due-to="Stephen Kestle">
-      Added support for using custom "Equator" objects in "EqualPredicate".
-    </action>
-    <action issue="COLLECTIONS-289" dev="bayard" type="add" due-to="Fredrik Kjellberg">
-      Added method "CollatingIterator#getIteratorIndex()".
-    </action>
-    <action issue="COLLECTIONS-256,COLLECTIONS-288" dev="bayard" type="fix" due-to="Paul Benedict">
-      Fixed javadoc for "ListUtils#transformedList(List)" to clarify that existing objects
-      in the list are not transformed.
-    </action>
-    <action issue="COLLECTIONS-286" dev="mbenson" type="add" due-to="Geoffrey De Smet">
-      Added method "CollectionUtils#extractSingleton(Collection)".
-    </action>
-    <action issue="COLLECTIONS-285" dev="tn" type="add" due-to="Christian Gruenberg">
-      Added serialization support for "TreeBidiMap".
-    </action>
-    <action issue="COLLECTIONS-280" dev="bayard" type="update" due-to="Chris Lewis">
-      The predicate that rejected an object to be added to a "PredicatedCollection"
-      is now contained in the respective exception message.
-    </action>
-    <action issue="COLLECTIONS-275" dev="tn" type="add" due-to="Stephen Kestle">
-      Added "IndexedCollection" collection decorator which provides a map-like
-      view on an existing collection.
-    </action>
-    <action issue="COLLECTIONS-272" dev="tn" type="add" due-to="Chaitanya Mutyala">
-      Added serialization support for "FixedOrderComparator" and "TransformingComparator".
-    </action>
-    <action issue="COLLECTIONS-266" dev="bayard" type="fix" due-to="Joerg Schaible">
-      "MultiKey" will now be correctly serialized/de-serialized.
-    </action>
-    <action issue="COLLECTIONS-265" dev="bayard" type="update" due-to="David Saff">
-      "TreeBag" will now only accept "Comparable" objects as input when used with natural ordering.
-    </action>
-    <action issue="COLLECTIONS-263" dev="tn" type="add" due-to="John Hunsley">
-      Added methods "MapUtils#populateMap(MultiMap, ...)" to support also "MultiMap" instances
-      as input.
-    </action>
-    <action issue="COLLECTIONS-262" dev="bayard" type="fix" due-to="Lisen Mu">
-      Fixed javadoc for methods "firstKey()" and "lastKey()" in class "AbstractLinkedMap".
-    </action>
-    <action issue="COLLECTIONS-261" dev="bayard" type="fix" due-to="ori">
-      "Flat3Map#remove(Object)" will now return the correct value mapped to the removed key
-      if the size of the map is less or equal 3.
-    </action>
-    <action issue="COLLECTIONS-260" dev="mbenson" type="add" due-to="Stephen Kestle">
-      Added constructor "TransformingComparator(Transformer)".
-    </action>
-    <action issue="COLLECTIONS-258" dev="tn" type="add" due-to="Nathan Blomquist">
-      Added "DualLinkedHashBidiMap" bidi map implementation.
-    </action>
-    <action issue="COLLECTIONS-255" dev="mbenson" type="fix" due-to="Henri Yandell">
-      Removed unused variables in "TreeBidiMap".
-    </action>
-    <action issue="COLLECTIONS-251,COLLECTIONS-321" dev="mbenson" type="update" due-to="Stephen Kestle">
-      The static factory methods have been renamed from "getInstance()" to a camel-case
-      version of the class name, e.g. "truePredicate()" for class "TruePredicate".
-    </action>
-    <action issue="COLLECTIONS-249" dev="bayard" type="fix" due-to="Joe Kelly">
-      "SetUniqueList.addAll(int, Collection)" now correctly add the collection at the
-      provided index.
-    </action>
-    <action issue="COLLECTIONS-242" dev="skestle" type="add">
-      Added "Equator" interface.
-    </action>
-    <action issue="COLLECTIONS-241" dev="brentworden" type="add" due-to="Elifarley Callado Coelho">
-      Added "PassiveExpiringMap" map decorator.
-    </action>
-    <action issue="COLLECTIONS-240" dev="bayard" type="update" due-to="Wouter de Vaal">
-      "MultiValueMap" is now serializable.
-    </action>
-    <action issue="COLLECTIONS-237" dev="tn" type="add" due-to="Nils Kaiser, Alan Mehlo">
-      Added method "MultiValueMap#iterator()" to return a flattened version of
-      "entrySet().iterator()". Clarified javadoc for "entrySet()" that the returned Entry
-      objects are unflattened, i.e. the Entry object for a given key contains all values
-      mapped to this key.
-    </action>
-    <action issue="COLLECTIONS-235" dev="bayard" type="add" due-to="Nathan Egge">
-      Added method "ListUtils#indexOf(List, Predicate)".
-    </action>
-    <action issue="COLLECTIONS-232" dev="bayard" type="fix" due-to="Mark Hindess">
-      Fixed several unit tests which were using parameters to "assertEquals(...)" in wrong order.
-    </action>
-    <action issue="COLLECTIONS-231" dev="tn" type="update" due-to="Torsten Curdt">
-      Return concrete class in static factory methods instead of base class interface
-      (except for Unmodifiable decorators).
-    </action>
-    <action issue="COLLECTIONS-230,COLLECTIONS-297,COLLECTIONS-318" dev="bayard" type="update" due-to="Stepan Koltsov,sebb">
-      "CollectionUtils#size(Collection)" now returns 0 when called with null as input.
-    </action>
-    <action issue="COLLECTIONS-229" dev="scolebourne" type="remove">
-      Removed deprecated classes and methods.
-    </action>
-    <action issue="COLLECTIONS-228" dev="scolebourne" type="fix">
-      "MultiValueMap#put(Object, Object)" and "MultiValueMap#putAll(Object, Collection)"
-      now correctly return if the map has changed by this operation.
-    </action>
-    <action issue="COLLECTIONS-226" dev="bayard" type="add" due-to="Vasily Ivanov">
-      Added method "ListOrderedMap#putAll(int, Map)".
-    </action>
-    <action issue="COLLECTIONS-225" dev="tn" type="add" due-to="Sam Berlin, Roger Kapsi">
-      Added new "Trie" interface with a first concrete implementation "PatriciaTrie"
-      together with decorators "Unmodifiable" and "Synchronized".
-    </action>
-    <action issue="COLLECTIONS-223" dev="bayard" type="update" due-to="Vasily Ivanov">
-      "CollectionUtils#addAll(...)" methods now return if the collection has been changed
-      by this operation.
-    </action>
-    <action issue="COLLECTIONS-221" dev="bayard" type="update" due-to="Pal Denes">
-      "CompositeCollection", "CompositeMap" and "CompositeSet" are now serializable.
-    </action>
-    <action issue="COLLECTIONS-219" dev="scolebourne" type="fix" due-to="Tom Leccese">
-      "CollectionUtils#removeAll" wrongly called "ListUtils#retainAll".
-    </action>
-    <action issue="COLLECTIONS-218" dev="skestle" type="update">
-      The "CollectionUtils#select(Collection, Predicate, Collection)" method will now
-      return the output collection.
-    </action>
-    <action issue="COLLECTIONS-217" dev="scolebourne" type="fix" due-to="Matt Bishop">
-      Calling "setValue(Object)" on any Entry returned by a "Flat3Map" will now
-      correctly set the value for the current entry.
-    </action>
-    <action issue="COLLECTIONS-216" dev="scolebourne" type="fix" due-to="Hendrik Maryns">
-      "MultiKey#toString()" will now use "Arrays#toString(List)".
-    </action>
-    <action issue="COLLECTIONS-213" dev="brentworden" type="add" due-to="Dusan Chromy">
-      Added support for resettable iterators in "IteratorIterable".
-    </action>
-    <action issue="COLLECTIONS-194" dev="bayard" type="add" due-to="Dave Meikle">
-      Added methods "MapUtils#populateMap(Map, Iterable, Transformer, ...)".
-    </action>
-    <action issue="COLLECTIONS-182" dev="mbenson" type="update" due-to="Jim Cakalic">
-      "CollectionUtils#forAllDo(Collection, Closure)" now returns the provided closure.
-    </action>
-    <action issue="COLLECTIONS-110,COLLECTIONS-243,COLLECTIONS-245,COLLECTIONS-247,
-                   COLLECTIONS-253,COLLECTIONS-273,COLLECTIONS-282" dev="multiple" type="update">
-      Make generic versions of all classes in collections.
-    </action>
-    <action issue="COLLECTIONS-8" dev="brentworden" type="add" due-to="Rune Peter Bjørnstad">
-      Added class "ComparatorPredicate".
-    </action>
-  </release>
-  <release version="3.2.2" date="2015-11-15" description="This is a security and bugfix release.">
-    <action issue="COLLECTIONS-580" dev="tn" type="update">
-      Serialization support for unsafe classes in the functor package is disabled
-      by default as this can be exploited for remote code execution attacks.
-      To re-enable the feature the system property "org.apache.commons.collections.enableUnsafeSerialization"
-      needs to be set to "true".
-      Classes considered to be unsafe are: CloneTransformer, ForClosure, InstantiateFactory,
-      InstantiateTransformer, InvokerTransformer, PrototypeCloneFactory,
-      PrototypeSerializationFactory, WhileClosure.
-    </action>
-    <action issue="COLLECTIONS-538" dev="tn" type="fix" due-to="Trejkaz">
-      "ExtendedProperties" will now use a privileged action to access the
-      "file.separator" system property. In case the class does not have
-      permission to read system properties, the "File#separator" field will
-      be used instead.
-    </action>
-    <action issue="COLLECTIONS-447" dev="tn" type="fix" due-to="Jeffrey Barnes">
-      Tree traversal with a TreeListIterator will not be affected anymore by
-      the removal of an element directly after a call to previous().
-    </action>
-    <action issue="COLLECTIONS-444" dev="tn" type="fix" due-to="Thomas Vahrst, John Vasileff">
-      SetUniqueList.set(int, Object) now works correctly if the object to be inserted
-      is already placed at the given position.
-    </action>
-    <action issue="COLLECTIONS-350" dev="bayard" type="fix" due-to="Michael Akerman">
-      Removed debug output in "MapUtils#getNumber(Map)".
-    </action>
-    <action issue="COLLECTIONS-335" dev="jochen" type="fix" due-to="sebb">
-      Fixed cache assignment for "TreeBidiMap#entrySet".
-    </action>
-    <action issue="COLLECTIONS-334" dev="jochen" type="fix" due-to="sebb">
-      Synchronized access to lock in "StaticBucketMap#size()".
-    </action>
-    <action issue="COLLECTIONS-307" dev="bayard" type="fix" due-to="Christian Semrau">
-      "SetUniqueList#subList()#contains(Object)" will now correctly check the subList
-      rather than the parent list.
-    </action>
-    <action issue="COLLECTIONS-304" dev="bayard" type="fix" due-to="Rafał Figas,Bjorn Townsend">
-      "SetUniqueList#set(int, Object)" will now correctly enforce the uniqueness constraint.
-    </action>
-    <action issue="COLLECTIONS-294" dev="bayard" type="fix" due-to="Benjamin Bentmann">
-      "CaseInsensitiveMap" will now convert input strings to lower-case in a
-      locale-independent manner.
-    </action>
-    <action issue="COLLECTIONS-266" dev="bayard" type="fix" due-to="Joerg Schaible">
-      "MultiKey" will now be correctly serialized/de-serialized.
-    </action>
-    <action issue="COLLECTIONS-261" dev="bayard" type="fix" due-to="ori">
-      "Flat3Map#remove(Object)" will now return the correct value mapped to the removed key
-      if the size of the map is less or equal 3.
-    </action>
-    <action issue="COLLECTIONS-249" dev="bayard" type="fix" due-to="Joe Kelly">
-      "SetUniqueList.addAll(int, Collection)" now correctly add the collection at the
-      provided index.
-    </action>
-    <action issue="COLLECTIONS-228" dev="scolebourne" type="fix">
-      "MultiValueMap#put(Object, Object)" and "MultiValueMap#putAll(Object, Collection)"
-      now correctly return if the map has changed by this operation.
-    </action>
-    <action issue="COLLECTIONS-219" dev="scolebourne" type="fix" due-to="Tom Leccese">
-      "CollectionUtils#removeAll" wrongly called "ListUtils#retainAll".
-    </action>
-    <action issue="COLLECTIONS-217" dev="scolebourne" type="fix" due-to="Matt Bishop">
-      Calling "setValue(Object)" on any Entry returned by a "Flat3Map" will now
-      correctly set the value for the current entry.
-    </action>
-  </release>
-  </body>
-</document>
+<?xml version="1.0"?>
+<!--
+     Licensed to the Apache Software Foundation (ASF) under one or more
+     contributor license agreements.  See the NOTICE file distributed with
+     this work for additional information regarding copyright ownership.
+     The ASF licenses this file to You 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.
+  -->
+<document>
+  <properties>
+    <title>Apache Commons Collections Release Notes</title>
+  </properties>
+  <body>
+  <release version="4.5" date="2019-MM-DD" description="Maintenance release.">
+    <action issue="COLLECTIONS-724" dev="ggregory" type="updated." due-to="Eitan Adler">
+      Simplify two remove-if loops #77.
+    </action>
+    <action issue="COLLECTIONS-726" dev="ggregory" type="updated." due-to=" liuhaozzu, Gary Gregory">
+      Add lambdas function to provide default values in MapUtils #81.
+    </action>
+  </release>
+  <release version="4.4" date="2019-07-05" description="Maintenance release.">
+    <action issue="COLLECTIONS-710" dev="ggregory" type="fix" due-to="Yu Shi, Gary Gregory">
+      NullPointerExceptions in CompositeCollection, CompositeSet, and CompositeMap.
+    </action>
+    <action issue="COLLECTIONS-715" dev="ggregory" type="add" due-to="morningmemo, Gary Gregory">
+      Implement Collection's removeIf().
+    </action>
+    <action issue="COLLECTIONS-718" dev="ggregory" type="update" due-to="Eitan Adler">
+      Fix LRUMap exception message.
+    </action>
+    <action issue="COLLECTIONS-719" dev="ggregory" type="add" due-to="Gary Gregory">
+      Create a PropertiesFactory and SortedPropertiesFactory.
+    </action>
+    <action issue="COLLECTIONS-719" dev="ggregory" type="add" due-to="Stephan Windmüller, Bruno P. Kinoshita">
+      Support Transformer for LazyList #52.
+    </action>
+    <action issue="COLLECTIONS-723" dev="ggregory" type="add" due-to="Eitan Adler, SOC, Bruno P. Kinoshita">
+      Make use of FunctionalInterface #48.
+    </action>
+    <action issue="COLLECTIONS-716" dev="ggregory" type="update" due-to="Sebb">
+      Don't include email address in Exception messages.
+    </action>
+  </release>
+  <release version="4.3" date="2018-12-21" description="Update from Java 7 to Java 8, bug fixes, and small changes.">
+    <action issue="COLLECTIONS-691" dev="kinow" type="fix" due-to="Eitan Adler">
+      Use boolean operator for boolean result.
+    </action>
+    <action issue="COLLECTIONS-688" dev="ggregory" type="update">
+      Update platform requirement from Java 7 to 8.
+    </action>
+    <action issue="COLLECTIONS-689" dev="ggregory" type="update" due-to="Richard Walker">
+      Link to Javadoc API broken.
+    </action>
+    <action issue="COLLECTIONS-692" dev="ggregory" type="update" due-to="Gary Gregory, Eitan Adler">
+      Replace use of deprecated Class#newInstance() PR #49.
+    </action>
+    <action issue="COLLECTIONS-696" dev="ggregory" type="add" due-to="Maxim Solodovnik">
+      AbstractReferenceMap made easier for subclassing; PR #51.
+    </action>
+    <action issue="COLLECTIONS-701" dev="ggregory" type="fix" due-to="Shin Hong, Don Jeba">
+      StackOverflowError in SetUniqueList.add() when it receives itself.
+    </action>
+    <action issue="COLLECTIONS-703" dev="ggregory" type="fix" due-to="Tomas Tulka">
+      The PassiveExpiringMap#put() method should return the previous record only if not expired.
+    </action>
+    <action issue="COLLECTIONS-706" dev="ggregory" type="fix" due-to="Richard Eckart de Castilho, Gary Gregory">
+      Add SetUtils.unmodifiableSet(T... items) method.
+    </action>
+    <action issue="COLLECTIONS-709" dev="ggregory" type="fix" due-to="Robert Wertman">
+      MultiSet.Entry::getCount() isn't 0 after removing the last element.
+    </action>
+  </release>
+  <release version="4.2" date="2018-07-11" description="Update from Java 6 to Java 7, bug fixes, and small changes.">
+    <action issue="COLLECTIONS-681" dev="kinow" type="add" due-to="Stephan Fuhrmann">
+      Add test for MultiSetUtils
+    </action>
+    <action issue="COLLECTIONS-599" dev="ggregory" type="fix" due-to="Tejas Patel, Saleem Akbar, Gary Gregory">
+      HashEntry array object naming data initialized with double the size during deserialization.
+    </action>
+    <action issue="COLLECTIONS-662" dev="chtompki" type="fix" due-to="Vamsi Kavuri">
+      Unit tests MapUtilsTest and ListIteratorWrapperTest no longer fail on Java 9.
+    </action>
+    <action issue="COLLECTIONS-661" dev="kinow" type="fix">
+      Intermittent test failures in Windows for HashSetValuedHashMap.
+    </action>
+    <action issue="COLLECTIONS-660" dev="kinow" type="fix">
+      Uncomment test in AbstractMapTest regarding LRUMap equals.
+    </action>
+    <action issue="COLLECTIONS-658" dev="britter" type="add">
+      Add Automatic-Module-Name MANIFEST entry for Java 9 compatibility.
+    </action>
+    <action issue="COLLECTIONS-656" dev="ggregory" type="fix">
+      Fix site build on Java 8.
+    </action>
+    <action issue="COLLECTIONS-653" dev="kinow" type="fix">
+      Update Javadoc to Build on Java 1.8.
+    </action>
+    <action issue="COLLECTIONS-606" dev="chtompki" type="fix" due-to="Vamsi Kavuri">
+      Build status, Coverage status and Maven central weren't in README.md
+    </action>
+    <action issue="COLLECTIONS-602" dev="kinow" type="update" due-to="John Mark">
+      Improve efficiency of DefaultedMap.get.
+    </action>
+    <action issue="COLLECTIONS-603" dev="kinow" type="update" due-to="Artem Konovalov">
+      Small improvements for generics, conditional statements, and warnings suppressions.
+    </action>
+    <action issue="COLLECTIONS-655" dev="ggregory" type="update">
+      Update platform from Java 6 to Java 7.
+    </action>
+    <action issue="COLLECTIONS-594" dev="ggregory" type="fix" due-to="Javen O'Neal">
+      Web site spelling error: MultiValuedMapeList.
+    </action>
+    <action issue="COLLECTIONS-597" dev="ggregory" type="fix" due-to="Enrique">
+       Correction of Javadoc for org.apache.commons.collections4.functors.CatchAndRethrowClosure.
+    </action>
+    <action issue="COLLECTIONS-589" dev="ggregory" type="add" due-to="Gary Gregory">
+      Add null-safe MapUtils.size(Map&lt;?, ?>) method.
+    </action>
+    <action issue="COLLECTIONS-586" dev="ggregory" type="add" due-to="Shailender Bathula, Gary Gregory">
+      PatriciaTrie prefixMap clear throws NullPointerException.
+    </action>
+    <action issue="COLLECTIONS-654" dev="ggregory" type="add">
+      Add class SortedProperties to sort keys.
+    </action>
+    <action issue="COLLECTIONS-666" dev="ggregory" type="update" due-to="BELUGA BEHR">
+      org.apache.commons.collections4.ListUtils.union(List, List) should pre-allocate result list.
+    </action>
+    <action issue="COLLECTIONS-669" dev="ggregory" type="update" due-to="BELUGA BEHR, Gary Gregory">
+      Update org.apache.commons.collections4.CollectionUtils.addAll(Collection&lt;C>, C[]) to addAll(Collection&lt;C>, C...).
+    </action>
+    <action issue="COLLECTIONS-668" dev="ggregory" type="add" due-to="Gary Gregory">
+      Add CollectionUtils containsAny method for primitive array: org.apache.commons.collections4.CollectionUtils.containsAny(Collection&lt;?>, T...).
+    </action>
+    <action issue="COLLECTIONS-575" dev="ggregory" type="add" due-to="Guram Savinov, Grzegorz Rożniecki, Bruno P. Kinoshita, Gary Gregory">
+      Synchronized queue wrapper in QueueUtils.
+    </action>
+    <action issue="COLLECTIONS-670" dev="ggregory" type="add" due-to="Gary Gregory">
+      Add org.apache.commons.collections4.IteratorUtils.first(Iterator).
+    </action>
+    <action issue="COLLECTIONS-671" dev="ggregory" type="add" due-to="Gary Gregory">
+      Add org.apache.commons.collections4.IterableUtils.first(Iterable).
+    </action>
+    <action issue="COLLECTIONS-678" dev="ggregory" type="fix" due-to="Oscar Luis Vera Pérez">
+      The verification of unsupported iterator methods is not complete.
+    </action>
+    <action issue="COLLECTIONS-673" dev="ggregory" type="fix" due-to="John Mark, Stephan Fuhrmann">
+      ListUtils.partition potential integer overflow.
+    </action>
+  </release>
+  <release version="4.1" date="2015-11-28" description="This is a security and minor release.">
+    <action issue="COLLECTIONS-508" dev="tn" type="add">
+      Added new interfaces "MultiValuedMap", "ListValuedMap" and "SetValuedMap"
+      as a replacement for "MultiMap". Decorators and implementations reside in
+      the "multimap" package and a "MultiMapUtils" class has been added.
+      The existing interface "MultiMap" as well as the concrete implementation
+      "MultiValueMap" has been deprecated.
+    </action>
+    <action issue="COLLECTIONS-551" dev="tn" type="update">
+      Deprecated various method in "CollectionUtils" in favor of similar
+      methods in the newly introduced "IterableUtils".
+    </action>
+    <action issue="COLLECTIONS-580" dev="tn" type="update">
+      Serialization support for unsafe classes in the functor package
+      has been removed as this can be exploited for remote code execution
+      attacks. Classes considered to be unsafe are: CloneTransformer,
+      ForClosure, InstantiateFactory, InstantiateTransformer, InvokerTransformer,
+      PrototypeCloneFactory, PrototypeSerializationFactory, WhileClosure.
+    </action>
+    <action issue="COLLECTIONS-580" dev="tn" type="fix">
+      Added validation when de-serializing a "MultiValueMap#ReflectionFactory":
+      only Collection classes are allowed, otherwise an UnsupportedOperationException
+      will be thrown during de-serialization.
+    </action>
+    <action issue="COLLECTIONS-567" dev="tn" type="add">
+      Added new MultiSet interface which is intended to be a replacement for
+      the Bag interface. The main difference is that a MultiSet is fully compatible
+      to the Collection contract.
+    </action>
+    <action issue="COLLECTIONS-576" dev="tn" type="fix" due-to="Stephan Roch">
+      Subclasses of MultiKey did not re-calculate their hashcode after de-serialization.
+    </action>
+    <action issue="COLLECTIONS-572" dev="tn" type="add">
+      Added set operations to "SetUtils": union, difference, intersection and disjunction.
+      The operations return a view of the result that is backed by the input sets.
+    </action>
+    <action issue="COLLECTIONS-570" dev="tn" type="update">
+      All constructors and static factory methods will now throw a "NullPointerException" if
+      a required input argument is null. Previously sometimes a "IllegalArgumentException" was used.
+    </action>
+    <action issue="COLLECTIONS-571" dev="tn" type="update">
+      Deprecated methods "synchronizedCollection(Collection)" and "unmodifiableCollection(Collection)"
+      in class "CollectionUtils", the corresponding methods in "java.util.Collections" should be used instead.
+    </action>
+    <action issue="COLLECTIONS-566" dev="tn" type="fix">
+      "IteratorUtils#collate(...)" methods did not use natural ordering when a
+      null comparator was provided.
+    </action>
+    <action issue="COLLECTIONS-557" dev="tn" type="add" due-to="Philippe Mouawad">
+      Added support to specify the initial size of a "LRUMap".
+    </action>
+    <action issue="COLLECTIONS-565" dev="tn" type="add">
+      Added decorators for "NavigableSet" interface.
+    </action>
+    <action issue="COLLECTIONS-464,COLLECTIONS-442" dev="tn" type="add">
+      Added new class "FluentIterable" to support a fluent API for manipulating
+      Iterable instances. Additionally various supporting methods have been
+      added to "IterableUtils" and "IteratorUtils".
+    </action>
+    <action issue="COLLECTIONS-464" dev="tn" type="add">
+      Added new "ZippingIterator" and factory methods "IteratorUtils#zippingIterator(...)".
+    </action>
+    <action issue="COLLECTIONS-464" dev="tn" type="add">
+      Added new decorator "SkippingIterator" and factory methods "IteratorUtils#skippingIterator(...)".
+    </action>
+    <action issue="COLLECTIONS-556" dev="tn" type="add">
+      Added method "SetUtils#newIdentityHashSet()" which returns a new identity HashSet
+      using reference-equality instead of object-equality.
+    </action>
+    <action issue="COLLECTIONS-562" dev="tn" type="update">
+      Upgraded minimum java requirement to Java 6 (up from Java 5).
+    </action>
+    <action issue="COLLECTIONS-395" dev="tn" type="add" due-to="David Hawthorne">
+      Added method "LRUMap#get(Object, boolean)" that allows to query the map
+      without affecting the least recently used order.
+    </action>
+    <action issue="COLLECTIONS-558" dev="tn" type="fix" due-to="Felix Rabe">
+      Changed return type of "ListOrderedSet#remove(int)" from Object to the generic type parameter.
+    </action>
+    <action issue="COLLECTIONS-555" dev="tn" type="fix" due-to="M Kim">
+      Added clarification to javadoc of "TreeBag#add(Object)" wrt null arguments.
+    </action>
+    <action issue="COLLECTIONS-427" dev="tn" type="add" due-to="Gonçalo Marques">
+      Added "toString(...)" methods to newly created "IterableUtils" and existing "IteratorUtils"
+      to get a string representation of an Iterable/Iterator instance similar to "Arrays#toString(...)".
+    </action>
+    <action issue="COLLECTIONS-427" dev="tn" type="fix">
+      Reverted performance improvement for "SetUniqueList#retainAll(Collection)"
+      introduced in 4.0. Added clarifying javadoc wrt runtime complexity instead.
+    </action>
+    <action issue="COLLECTIONS-426" dev="tn" type="fix">
+      Reverted performance improvement for "ListOrderedSet#retainAll(Collection)"
+      introduced in 4.0. Added clarifying javadoc wrt runtime complexity instead.
+    </action>
+    <action issue="COLLECTIONS-530" dev="tn" type="fix" due-to="Erik">
+      Added a Builder for "PredicatedCollection". Elements added to the builder
+      that fail the predicate will not throw an IllegalArgumentException. The builder
+      supports creating predicated lists, bags, sets and queues.
+    </action>
+    <action issue="COLLECTIONS-545" dev="tn" type="fix" due-to="Oswaldo Olivo">
+      Documented runtime complexity of "CollectionUtils#removeAll(Collection, Collection).
+    </action>
+    <action issue="COLLECTIONS-543" dev="tn" type="fix">
+      "AbstractCollectionDecorator" doesn't forward equals and hashCode anymore.
+    </action>
+    <action issue="COLLECTIONS-544" dev="tn" type="fix" due-to="Oswaldo Olivo">
+      Documented runtime complexity of "CollectionUtils#retainAll(Collection, Collection).
+    </action>
+    <action issue="COLLECTIONS-542" dev="tn" type="fix">
+      "AbstractHashedMap" still inherits from "AbstractMap", contrary to what
+      the class javadoc stated. The inheritance will now be removed in v5.0.
+    </action>
+    <action issue="COLLECTIONS-539" dev="tn" type="add" due-to="Guram Savinov">
+      Changed scope of "CircularFifoQueue#isAtFullCapacity()" to public.
+    </action>
+    <action issue="COLLECTIONS-525" dev="tn" type="fix" due-to="Zigler Zhang">
+      The map returned by "PatriciaTrie#prefixMap()" did not contain all keys
+      that are prefixed by the given search key in some rare cases.
+    </action>
+    <action issue="COLLECTIONS-511" dev="tn" type="add" due-to="Nathan Blomquist, Brent Worden">
+      Added new methods "IterableUtils#partition(...)" to partition an input collection
+      into separate output collections based on evaluation of one or more predicates.
+    </action>
+    <action issue="COLLECTIONS-537" dev="tn" type="fix" due-to="Frank Jakop">
+      Harmonized signature of factory methods for functor-related classes which take
+      a collection as input with their array counterparts.
+    </action>
+    <action issue="COLLECTIONS-540" dev="tn" type="fix" due-to="Daniel Stewart, Issam El Atif">
+      Added overloaded method "CollectionUtils#get(Enumeration, int)" and simplified
+      code for "CollectionUtils#get(Object, int)".
+    </action>
+    <action issue="COLLECTIONS-536" dev="tn" type="fix" due-to="Tagir Valeev">
+      Improved check for null input in "MapUtils#putAll(Map, Object[])".
+    </action>
+    <action issue="COLLECTIONS-534" dev="tn" type="fix" due-to="Oswaldo Olivo">
+      Added clarifying javadoc wrt runtime complexity of "CollectionBag#retainAll".
+    </action>
+    <action issue="COLLECTIONS-529" dev="tn" type="add" due-to="Alexander Muthmann, Dipanjan Laha">
+      Added methods "removeAll(...)" and "retainAll(...)" to "CollectionUtils" that perform
+      equality checks using the provided "Equator" object instead of "Object#equals()".
+    </action>
+    <action issue="COLLECTIONS-531" dev="tn" type="fix" due-to="Dipanjan Laha">
+      Use correct type bounds in
+      "CollectionUtils#isEqualCollection(Collection, Collection, Equator)" to
+      prevent a "ClassCastException" at runtime for invalid inputs.
+    </action>
+    <action issue="COLLECTIONS-523" dev="tn" type="fix" due-to="Thiago Andrade">
+      Removed unneeded private method in "PassiveExpiringMap".
+    </action>
+    <action issue="COLLECTIONS-516" dev="tn" type="fix" due-to="Cyrille Artho">
+      Added clarification to the javadoc of "MapUtils#toProperties(Map)" in case
+      of null keys/values.
+    </action>
+    <action issue="COLLECTIONS-524" dev="tn" type="fix" due-to="J Goodfellow">
+      "ListOrderedSet#listOrderedSet(List)" did not remove duplicates from the
+      input list as advertised in the javadoc.
+    </action>
+    <action issue="COLLECTIONS-521" dev="tn" type="fix" due-to="Maxime Nay">
+      "MultiKeyMap" was throwing a "NullPointerException" for various operations
+      if two key arguments have been used and the second was "null".
+    </action>
+    <action issue="COLLECTIONS-522" dev="tn" type="fix" due-to="Erik">
+      Updated code example for "PredicatedList".
+    </action>
+    <action issue="COLLECTIONS-512" dev="tn" type="fix" due-to="Cyrille Artho">
+      "TransformingComparator" and "FixedOrderComparator" did not comply with
+      the contract of "Object#equals".
+    </action>
+    <action issue="COLLECTIONS-510" dev="tn" type="fix" due-to="Hollis Waite">
+      Fix compilation errors when using source level 1.8 and a recent java 8 compiler.
+    </action>
+    <action issue="COLLECTIONS-509" dev="tn" type="fix">
+      Clarified javadoc of "CollectionBag" wrt changes from the original Bag interface.
+    </action>
+    <action issue="COLLECTIONS-507" dev="tn" type="fix" due-to="Gerson">
+      Removed wrong type bounds for "ComparatorUtils#chainedComparator(...)".
+    </action>
+    <action issue="COLLECTIONS-506" dev="tn" type="fix" due-to="Anthony Communier">
+      Added javadoc clarification to class "CollectionUtils" that input objects which
+      override "Object#equals(Object)" must also maintain the general contract of
+      "Object#hashCode()" as various utility methods take advantage of sets/maps/bags.
+    </action>
+    <action issue="COLLECTIONS-503" dev="tn" type="add" due-to="Josh Cain">
+      Added new transformer "IfTransformer" and factory methods "TransformerUtils#ifTransformer(...)"
+      which replace "TransformerUtils#switchTransformer(Predicate, Transformer, Transformer)".
+    </action>
+    <action issue="COLLECTIONS-471" dev="tn" type="add" due-to="Radford Tam">
+      Added new decorator "BoundedIterator" and factory methods "IteratorUtils#boundedIterator(...)".
+    </action>
+  </release>
+  <release version="4.0" date="2013-11-27" description="
+This is a major release: It combines bug fixes, new features and
+changes to existing features.
+
+Most notable changes are: use of generics and other language features introduced in Java 5 (varargs, Iterable),
+removed deprecated classes / methods and features which are now supported by the JDK,
+replaced Buffer interface with java.util.Queue,
+added concept of split maps with respective interfaces Put / Get (see also package splitmap),
+added new Trie interface together with an implementation of a Patricia Trie.
+
+Because of the base package name change, this release can be used together
+with earlier versions of Commons Collections.
+The minimal version of the Java platform required to compile and use
+Commons Collections is Java 5.
+Users are encouraged to upgrade to this version as, in addition to new
+features, this release includes numerous bug fixes.
+  ">
+    <action issue="COLLECTIONS-502" dev="tn" type="update">
+      Resolved generic parameter inconsistency for various static fields, e.g. BagUtils.EMPTY_BAG,
+      TruePredicate.INSTANCE and many others. All accessible static fields use raw types so that
+      they can be used directly without explicit casting. To avoid compiler warnings about unchecked
+      conversion and/or rawtypes use the corresponding factory methods, e.g. BagUtils.emptyBag().
+    </action>
+    <action issue="COLLECTIONS-501" dev="tn" type="update">
+      Renamed methods "V MultiKeyMap#remove(Object, Object, ...)" to
+      "V MultiKeyMap#removeMultiKey(Object, Object, ...)" to avoid future conflicts
+      with a default method of the Map interface in Java 8.
+    </action>
+    <action issue="COLLECTIONS-500" dev="tn" type="update">
+      Renamed "V MultiMap#remove(K, V)" to "boolean MultiMap#removeMapping(K, V)"
+      to avoid future conflicts with a default method of the Map interface in Java 8.
+    </action>
+    <action issue="COLLECTIONS-499" dev="tn" type="update">
+      Refactored the test framework for Bag implementations to extend from
+      "AbstractCollectionTest" by decorating the concrete Bag instance with
+      a CollectionBag or CollectionSortedBag.
+    </action>
+    <action issue="COLLECTIONS-498" dev="tn" type="fix">
+      "CollectionBag" will now also respect the contract of the decorated bag in case
+      a null argument is provided to either removeAll or retainAll.
+    </action>
+    <action issue="COLLECTIONS-497" dev="tn" type="add">
+      Added bag decorator "CollectionSortedBag" which decorates a SortedBag to make it
+      comply with the Collection contract.
+    </action>
+    <action issue="COLLECTIONS-496" dev="tn" type="update">
+      "UnmodifiableBoundedCollection" does now also implement the marker interface "Unmodifiable"
+      similar as all other unmodifiable decorators.
+    </action>
+    <action issue="COLLECTIONS-495" dev="tn" type="fix">
+      "UnmodifiableTrie#unmodifiableTrie(Trie)" will not decorate again an already
+      unmodifiable Trie. Also the return type has been changed to "Trie" to be consistent
+      with other Unmodifiable decorators.
+    </action>
+    <action issue="COLLECTIONS-494" dev="tn" type="update" due-to="Emmanuel Bourg">
+      Moved "Equator" interface to base package for consistency.
+    </action>
+    <action issue="COLLECTIONS-488" dev="tn" type="add" due-to="Josh Cain">
+      Added "CollectionsUtils#matchesAll(Iterable, Predicate)" to test if all elements
+      of a collection match a given predicate.
+    </action>
+    <action issue="COLLECTIONS-485" dev="tn" type="fix" due-to="Hollis Waite">
+      Accept wildcard input where possible, e.g. in copy-constructors, Unmodifiable* decorators
+      and iterators.
+    </action>
+    <action issue="COLLECTIONS-481" dev="tn" type="fix" due-to="Hollis Waite">
+      No collision detection/resolution was performed when calling "CompositeSet#addComposited(...)"
+      with more than one Set as argument. Additionally use varargs parameters instead of arrays
+      in CompositeSet and CompositeCollection constructor and addComposited method.
+    </action>
+    <action issue="COLLECTIONS-480" dev="tn" type="update" due-to="Hollis Waite">
+      Narrow return type of "BidiMap#values()" to Set as the values are required to be unique.
+    </action>
+    <action issue="COLLECTIONS-475" dev="tn" type="fix">
+      Fixed conversion of timeout parameters in "PassiveExpiringMap".
+    </action>
+    <action issue="COLLECTIONS-474" dev="sebb" type="fix" due-to="Ning Chen">
+      Exception in "ListOrderedMap#putAll" if map contains null values.
+    </action>
+    <action issue="COLLECTIONS-473" dev="tn" type="update" due-to="sebb">
+      Made field "collection" in class "AbstractCollectionDecorator" private and added
+      setter "setCollection(Collection)" with scope protected to set the decorated collection
+      during de-serialization.
+    </action>
+    <action issue="COLLECTIONS-472" dev="tn" type="fix" due-to="Adrian Nistor">
+      Improved performance of "AbstractMapBag#containsAll(Collection)" by returning immediately
+      after a difference has been found.
+    </action>
+    <action issue="COLLECTIONS-470" dev="tn" type="update" due-to="sebb">
+      Renamed class "TransformedMap" in package "splitmap" to "TransformedSplitMap" to avoid
+      name clash with similar class in package "map".
+    </action>
+    <action issue="COLLECTIONS-468" dev="tn" type="add">
+      Added bag decorator "CollectionBag" which decorates a bag to make it comply with the
+      Collection contract.
+    </action>
+    <action issue="COLLECTIONS-466" dev="tn" type="update">
+      Replaced "Collection" with "Iterable" for method arguments where applicable.
+    </action>
+    <action issue="COLLECTIONS-463" dev="tn" type="add" due-to="Andy Seaborne, Claude Warren">
+      Added "PushbackIterator" decorator to support pushback of elements during iteration.
+    </action>
+    <action issue="COLLECTIONS-462" dev="tn" type="add" due-to="Andy Seaborne, Claude Warren">
+      Added "PeekingIterator" decorator to support one-element lookahead during iteration.
+    </action>
+    <action issue="COLLECTIONS-461" dev="tn" type="fix" due-to="Matt Benson, sebb">
+      Added additional clarification to javadoc of interface "Put" wrt return type of
+      "put(Object, Object)" method.
+    </action>
+    <action issue="COLLECTIONS-460" dev="tn" type="update">
+      Changed "IteratorChain" to use internally a "Queue" instead of a "List". Iterators are
+      removed from the queue once used and can be garbage collected after being exhausted.
+      Additionally removed the methods "setIterator(Iterator)" and "getIterators()".
+    </action>
+    <action issue="COLLECTIONS-459" dev="tn" type="update" due-to="sebb">
+      Removed method "setArray(Object)" in class ArrayIterator and method "setArray(Object[])"
+      in class ObjectArrayIterator and made fields array, startIndex and endIndex final.
+    </action>
+    <action issue="COLLECTIONS-458" dev="sebb" type="remove">
+      Removed unused class "AbstractUntypedCollectionDecorator&lt;E, D&gt;".
+    </action>
+    <action issue="COLLECTIONS-456" dev="tn" type="add">
+      Added methods "ListUtils#longestCommonSubsequence(...)" to get the longest common subsequence
+      of arbitrary lists or CharSequences.
+    </action>
+    <action issue="COLLECTIONS-455" dev="sebb" type="update">
+      Changed scope of fields to private where appropriate.
+    </action>
+    <action issue="COLLECTIONS-454" dev="tn" type="update">
+      An iterator over a "Flat3Map#entrySet()" will now return
+      independent Map.Entry objects that will not change anymore when
+      the iterator progresses.
+    </action>
+    <action issue="COLLECTIONS-453" dev="tn" type="update">
+      Several closure and transformer implementations in the functors package
+      will now copy an array as input to their constructor (e.g. ChainedClosure).
+    </action>
+    <action issue="COLLECTIONS-452" dev="tn" type="update">
+      Change base package to org.apache.commons.collections4.
+    </action>
+    <action issue="COLLECTIONS-451" dev="tn" type="update">
+      The constructors for all Utils classes are now private to prevent instantiation.
+    </action>
+    <action issue="COLLECTIONS-450" dev="tn" type="add" due-to="J. Moldawski">
+      Added methods "forAllButLastDo(Collection, Closure)" and "forAllButLastDo(Iterator, Closure)"
+      to class "CollectionUtils".
+    </action>
+    <action issue="COLLECTIONS-447" dev="tn" type="fix" due-to="Jeffrey Barnes">
+      Tree traversal with a TreeListIterator will not be affected anymore by
+      the removal of an element directly after a call to previous().
+    </action>
+    <action issue="COLLECTIONS-446" dev="tn" type="add" due-to="Matt Lachman">
+      Added method "CollectionUtils#isEqualCollection(Collection, Collection, Equator)".
+    </action>
+    <action issue="COLLECTIONS-445" dev="tn" type="fix">
+      Adapt and/or ignore several unit tests when run on a IBM J9 VM (specification version 1.6.0)
+      due to a faulty "java.util.TreeMap" implementation.
+    </action>
+    <action issue="COLLECTIONS-444" dev="tn" type="fix" due-to="Thomas Vahrst, John Vasileff">
+      SetUniqueList.set(int, E) now works correctly if the object to be inserted
+      is already placed at the given position.
+    </action>
+    <action issue="COLLECTIONS-441" dev="tn" type="fix" due-to="Thomas Vahrst">
+      MultiKeyMap.clone() now correctly calls super.clone().
+    </action>
+    <action issue="COLLECTIONS-436" dev="tn" type="add" due-to="Arman Sharif">
+      Added "emptyIfNull" methods to classes "CollectionUtils", "ListUtils", "SetUtils"
+      and "MapUtils".
+    </action>
+    <action issue="COLLECTIONS-433" dev="tn" type="fix" due-to="Jeffrey Barnes">
+      Improve performance of "TreeList#addAll" and "TreeList(Collection)" by converting
+      the input collection into an AVL tree and efficiently merge it into the existing tree.
+    </action>
+    <action issue="COLLECTIONS-432" dev="tn" type="update">
+      Replaced "Buffer" interface with "java.util.Queue". Kept "CircularFifoQueue"
+      as well as "Predicated", "Transformed" and "Unmodifiable" decorators.
+    </action>
+    <action issue="COLLECTIONS-429,COLLECTIONS-434" dev="tn" type="add" due-to="Adrian Nistor, Mert Guldur">
+      Added method "CollectionUtils#containsAll(Collection, Collection)" with guaranteed
+      runtime complexity of O(n + m) and space complexity of O(n). This method may yield much
+      better performance than "Collection#containsAll(Collection)" depending on the use-case and
+      type of collection used.
+    </action>
+    <action issue="COLLECTIONS-427" dev="brentworden" type="fix" due-to="Mert Guldur">
+      Fixed performance issue in "SetUniqueList#retainAll" method for large collections.
+    </action>
+    <action issue="COLLECTIONS-426" dev="brentworden" type="fix" due-to="Adrian Nistor">
+      Fixed performance issue in "ListOrderedSet#retainAll" method for large collections.
+    </action>
+    <action issue="COLLECTIONS-425" dev="tn" type="fix" due-to="Adrian Nistor">
+      Improved performance of "ListOrderedMap#remove(Object)" method.
+    </action>
+    <action issue="COLLECTIONS-424" dev="tn" type="update" due-to="Michael Pradel">
+      "CompositeSet" does not inherit from "CompositeCollection" anymore. The inner class
+      "SetMutator" has been updated accordingly.
+    </action>
+    <action issue="COLLECTIONS-422" dev="tn" type="add" due-to="Benoit Corne">
+      Added method "CollectionUtils#permutations(Collection)" and class "PermutationIterator"
+      to generate unordered permutations of a collection.
+    </action>
+    <action issue="COLLECTIONS-421" dev="tn" type="fix" due-to="Benedikt Ritter">
+      Update javadoc for "ListUtils#lazyList()" and "ListUtils#fixedSizeList()".
+    </action>
+    <action issue="COLLECTIONS-419" dev="tn" type="fix" due-to="Adrian Nistor">
+      Added clarifying javadoc wrt runtime complexity of "AbstractDualBidiMap#retainAll".
+    </action>
+    <action issue="COLLECTIONS-417" dev="tn" type="fix" due-to="Adrian Nistor">
+      Added clarifying javadoc wrt runtime complexity of "AbstractLinkedList#retainAll".
+    </action>
+    <action issue="COLLECTIONS-415" dev="tn" type="fix" due-to="Adrian Nistor">
+      Added clarifying javadoc wrt runtime complexity of "AbstractLinkedList#removeAll".
+    </action>
+    <action issue="COLLECTIONS-414" dev="tn" type="fix">
+      Fixed several compilation issues with older Java 1.6 compilers.
+    </action>
+    <action issue="COLLECTIONS-413" dev="tn" type="fix" due-to="Adrian Nistor">
+      Improved performance of "removeAll()" method for sets returned by "DualHashBidiMap#entrySet()".
+    </action>
+    <action issue="COLLECTIONS-412" dev="tn" type="fix" due-to="Adrian Nistor">
+      Improved performance of "CollectionUtils#subtract" methods.
+    </action>
+    <action issue="COLLECTIONS-411" dev="tn" type="fix" due-to="Adrian Nistor">
+      Fixed possible "IndexOutOfBoundsException" in "ListOrderedMap#putAll".
+    </action>
+    <action issue="COLLECTIONS-410" dev="tn" type="fix" due-to="Adrian Nistor">
+      Improved performance of "SetUniqueList#addAll" method.
+    </action>
+    <action issue="COLLECTIONS-409" dev="tn" type="fix" due-to="Adrian Nistor">
+      Improved performance of "ListOrderedSet#addAll" method.
+    </action>
+    <action issue="COLLECTIONS-408" dev="tn" type="fix" due-to="Adrian Nistor">
+      Improved performance of "SetUniqueList#removeAll".
+    </action>
+    <action issue="COLLECTIONS-407" dev="tn" type="fix" due-to="Adrian Nistor">
+      Improved performance of "ListOrderedSet#remove(Object)" in case the object is
+      not contained in the Set.
+    </action>
+    <action issue="COLLECTIONS-406" dev="tn" type="fix" due-to="Adrian Nistor">
+      Improved performance of "ListUtils#subtract" method.
+    </action>
+    <action issue="COLLECTIONS-405" dev="brentworden" type="add" due-to="Adam Dyga">
+      Added "ListUtils#select" and "ListUtils#selectRejected" methods.
+    </action>
+    <action issue="COLLECTIONS-404" dev="luc" type="add" due-to="Jordane Sarda">
+      Added an implementation of Eugene Myers difference algorithm in package
+      o.a.c.c.sequence.
+    </action>
+    <action issue="COLLECTIONS-400" dev="tn" type="fix" due-to="Shin Hwei Tan">
+      Added missing null check in "CollectionUtils#addIgnoreNull(Collection, Object)".
+    </action>
+    <action issue="COLLECTIONS-399" dev="tn" type="add" due-to="Sebb">
+      Added new method "get(int)" to "CircularFifoQueue".
+    </action>
+    <action issue="COLLECTIONS-396" dev="tn" type="add" due-to="Jeff Rodriguez">
+      Added "LazyIteratorChain" iterator.
+    </action>
+    <action issue="COLLECTIONS-393" dev="tn" type="add" due-to="Chris Shayan">
+      Added "ListUtils#partition" method to split a List into consecutive sublists.
+    </action>
+    <action issue="COLLECTIONS-391" dev="tn" type="fix" due-to="Shin Hwei Tan">
+      Fixed javadoc for "MapUtils#toProperties(Map)".
+    </action>
+    <action issue="COLLECTIONS-389" dev="tn" type="fix" due-to="Shin Hwei Tan">
+      Clarified javadoc for "TransformerUtils#mapTransformer" for null input.
+    </action>
+    <action issue="COLLECTIONS-388" dev="tn" type="fix" due-to="Shin Hwei Tan">
+      Clarified javadoc for "FactoryUtils#prototypeFactory" for null input.
+    </action>
+    <action issue="COLLECTIONS-384" dev="ggregory" type="fix" due-to="Shin Hwei Tan">
+      Fixed inconsistent javadoc for "MapUtils#synchronizedMap(Map)".
+    </action>
+    <action issue="COLLECTIONS-383" dev="tn" type="add" due-to="Adrian Cumiskey">
+      Added "CollectionUtils#forAllDo" implementation which takes an "Iterator" as input.
+    </action>
+    <action issue="COLLECTIONS-382" dev="tn" type="update" due-to="Olivier Lamy">
+      Change maven coordinates to "org.apache.commons.commons-collections4".
+    </action>
+    <action issue="COLLECTIONS-381" dev="sebb" type="update" due-to="Olivier Lamy">
+      Move the project structure to a standard maven layout.
+    </action>
+    <action issue="COLLECTIONS-380" dev="tn" type="fix" due-to="Dave Brosius">
+      Fixed infinite loop when calling "UnmodifiableBoundedCollection#unmodifiableBoundedCollection()".
+    </action>
+    <action issue="COLLECTIONS-379" dev="tn" type="fix" due-to="Shin Hwei Tan">
+      Fixed javadoc for several methods wrt expected NullPointerExceptions.
+    </action>
+    <action issue="COLLECTIONS-375" dev="tn" type="add" due-to="Ivan Hristov">
+      Added method "ListUtils#defaultIfNull(List, List)".
+    </action>
+    <action issue="COLLECTIONS-372" dev="tn" type="update">
+      TransformingComparator now supports different types for its input/output values.
+    </action>
+    <action issue="COLLECTIONS-364" dev="sebb" type="fix">
+      "DualTreeBidiMap" now uses the correct comparator for the reverse map during de-serialization.
+    </action>
+    <action issue="COLLECTIONS-363" dev="sebb" type="fix">
+      "TransformedMap" in the package "splitmap" can now be serialized.
+    </action>
+    <action issue="COLLECTIONS-362" dev="brentworden" type="update" due-to="Jean-Noel Rouvignac">
+      "CollectionUtils#filter(Iterable, Predicate)" will now return whether the collection
+      has been modified.
+    </action>
+    <action issue="COLLECTIONS-361" dev="tn" type="add" due-to="Jean-Noel Rouvignac">
+      Add method "CollectionUtils#filterInverse(Iterable, Predicate)".
+    </action>
+    <action issue="COLLECTIONS-360" dev="jochen" type="fix" due-to="Sai Zhang">
+      "FilterListIterator#hasNext" does not throw a NullPointerException anymore
+      to comply to the Java iterator specification.
+    </action>
+    <action issue="COLLECTIONS-359" dev="bayard" type="fix" due-to="Mark Shead">
+      "ListUtils#intersection(List, List)" will now also work correctly if there
+      are duplicate elements in the provided lists.
+    </action>
+    <action issue="COLLECTIONS-352" dev="bayard" type="fix" due-to="Adam Gent">
+      "AbstractCollectionDecorator" will now use internally "decorated()" to access
+      the decorated collection.
+    </action>
+    <action issue="COLLECTIONS-351" dev="bayard" type="remove" due-to="Henri Yandell">
+      Removed features which are now supported by the JDK.
+    </action>
+    <action issue="COLLECTIONS-350" dev="bayard" type="fix" due-to="Michael Akerman">
+      Removed debug output in "MapUtils#getNumber(Map)".
+    </action>
+    <action issue="COLLECTIONS-348" dev="brentworden" type="fix" due-to="Paul Benedict">
+      Fixed javadoc for all "transformedXXX(XXX)" methods in the respective Utils classes
+      to clarify that existing objects in the list are not transformed.
+    </action>
+    <action issue="COLLECTIONS-343" dev="mbenson" type="fix" due-to="Goran Hacek">
+      Singleton classes in package "functors" are now correctly de-serialized.
+    </action>
+    <action issue="COLLECTIONS-341" dev="mbenson" type="update" due-to="Goran Hacek">
+      "NOPClosure" is now a final class.
+    </action>
+    <action issue="COLLECTIONS-340" dev="mbenson" type="fix" due-to="Goran Hacek">
+      Removed broken methods "equals(Object)" and "hashCode()" in class "NOPClosure".
+    </action>
+    <action issue="COLLECTIONS-336" dev="bayard" type="fix" due-to="sebb">
+      Simplified exceptions as the cause is available from the parent.
+    </action>
+    <action issue="COLLECTIONS-335" dev="jochen" type="fix" due-to="sebb">
+      Fixed cache assignment for "TreeBidiMap#entrySet".
+    </action>
+    <action issue="COLLECTIONS-334" dev="jochen" type="fix" due-to="sebb">
+      Synchronized access to lock in "StaticBucketMap#size()".
+    </action>
+    <action issue="COLLECTIONS-332" dev="jochen" type="fix" due-to="Tom Parker">
+      Added clarification to javadoc of "ListOrderedMap" that "IdentityMap" and
+      "CaseInsensitiveMap" are not supported.
+    </action>
+    <action issue="COLLECTIONS-331" dev="jochen" type="fix" due-to="Michael Krkoska">
+      Improve javadoc of "CollatingIterator" wrt the used "Comparator" and throw a
+      NullPointerException in "CollatingIterator#least" if no comparator is set.
+    </action>
+    <action issue="COLLECTIONS-330" dev="mbenson" type="fix" due-to="Joerg Schaible">
+      "LRUMap#keySet()#remove(Object)" will not throw a "ConcurrentModificationException" anymore.
+    </action>
+    <action issue="COLLECTIONS-328" dev="bayard" type="fix" due-to="Thomas Rogan, Jilles van Gurp">
+      Improved performance of "ListUtils#intersection(List, List)".
+    </action>
+    <action issue="COLLECTIONS-327" dev="brentworden" type="add" due-to="sebb">
+      Added serialVersionUID fields for "CompositeCollection", "CompositeSet",
+      "EmptyMapMutator", "EmptySetMutator".
+    </action>
+    <action issue="COLLECTIONS-324" dev="tn" type="update" due-to="sebb">
+      Fields transformer and decorated in class "TransformingComparator" are now final.
+    </action>
+    <action issue="COLLECTIONS-323" dev="jochen" type="fix" due-to="Maarten Brak">
+      Changed behavior of "CaseInsensitiveMap" constructor to be compliant with "HashMap"
+      in case the initial capacity is set to zero.
+    </action>
+    <action issue="COLLECTIONS-322" dev="tn" type="add" due-to="Thomas Vahrst">
+      Added NodeListIterator and convenience methods in IteratorUtils to iterate over
+      a org.w3c.dom.NodeList.
+    </action>
+    <action issue="COLLECTIONS-320" dev="bayard" type="fix" due-to="sebb">
+      Improved performance of "StaticBucketMap#putAll(Map)" by iterating over the entry set.
+    </action>
+    <action issue="COLLECTIONS-319" dev="bayard" type="fix" due-to="sebb">
+      Avoid redundant null check in "IteratorUtils#getIterator(Object)".
+    </action>
+    <action issue="COLLECTIONS-317" dev="bayard" type="fix" due-to="sebb">
+      Use a private method to populate the object in "AbstractHashedMap(Map)".
+    </action>
+    <action issue="COLLECTIONS-316" dev="bayard" type="fix" due-to="ori">
+      Fixed javadoc of "LRUMap" wrt to the maxSize parameter of the constructor.
+    </action>
+    <action issue="COLLECTIONS-313" dev="brentworden" type="add" due-to="David J. M. Karlsen">
+      Added new abstract class "CatchAndRethrowClosure" that re-throws any checked exception
+      as unchecked "FunctorException".
+    </action>
+    <action issue="COLLECTIONS-312" dev="tn" type="fix" due-to="Peter Lawrey, Gary Gregory">
+      Use of final keyword where applicable, minor performance improvements by properly
+      initializing the capacity of newly created collections when known in advance.
+    </action>
+    <action issue="COLLECTIONS-307" dev="tn" type="update" due-to="Christian Semrau, Thomas Vahrst">
+      "SetUniqueList#subList()" will now return an unmodifiable list as changes to it
+      may invalidate the parent list.
+    </action>
+    <action issue="COLLECTIONS-307" dev="bayard" type="fix" due-to="Christian Semrau">
+      "SetUniqueList#subList()#contains(Object)" will now correctly check the subList
+      rather than the parent list.
+    </action>
+    <action issue="COLLECTIONS-306" dev="brentworden" type="add" due-to="Chris Shayan">
+      Added method "CollectionUtils#subtract(Iterable, Iterable, Predicate)".
+    </action>
+    <action issue="COLLECTIONS-304" dev="bayard" type="fix" due-to="Rafał Figas,Bjorn Townsend">
+      "SetUniqueList#set(int, Object)" will now correctly enforce the uniqueness constraint.
+    </action>
+    <action issue="COLLECTIONS-303" dev="bayard" type="fix" due-to="Emmanuel Bourg">
+      Improved javadoc for "Unmodifiable*" classes wrt behavior when the users tries
+      to modify the collection.
+    </action>
+    <action issue="COLLECTIONS-298" dev="bayard" type="update" due-to="Benjamin Bentmann">
+      Calling "CollectionUtils#sizeIsEmpty(null)" will now return true.
+    </action>
+    <action issue="COLLECTIONS-296" dev="tn" type="add" due-to="Julius Davies">
+      Added methods "CollectionUtils#collate(...)" to merge two sorted Collections
+      into a sorted List using the standard O(n) merge algorithm.
+    </action>
+    <action issue="COLLECTIONS-294" dev="bayard" type="fix" due-to="Benjamin Bentmann">
+      "CaseInsensitiveMap" will now convert input strings to lower-case in a
+      locale-independant manner.
+    </action>
+    <action issue="COLLECTIONS-293" dev="tn" type="add" due-to="Stephen Kestle">
+      Added support for using custom "Equator" objects in "EqualPredicate".
+    </action>
+    <action issue="COLLECTIONS-289" dev="bayard" type="add" due-to="Fredrik Kjellberg">
+      Added method "CollatingIterator#getIteratorIndex()".
+    </action>
+    <action issue="COLLECTIONS-256,COLLECTIONS-288" dev="bayard" type="fix" due-to="Paul Benedict">
+      Fixed javadoc for "ListUtils#transformedList(List)" to clarify that existing objects
+      in the list are not transformed.
+    </action>
+    <action issue="COLLECTIONS-286" dev="mbenson" type="add" due-to="Geoffrey De Smet">
+      Added method "CollectionUtils#extractSingleton(Collection)".
+    </action>
+    <action issue="COLLECTIONS-285" dev="tn" type="add" due-to="Christian Gruenberg">
+      Added serialization support for "TreeBidiMap".
+    </action>
+    <action issue="COLLECTIONS-280" dev="bayard" type="update" due-to="Chris Lewis">
+      The predicate that rejected an object to be added to a "PredicatedCollection"
+      is now contained in the respective exception message.
+    </action>
+    <action issue="COLLECTIONS-275" dev="tn" type="add" due-to="Stephen Kestle">
+      Added "IndexedCollection" collection decorator which provides a map-like
+      view on an existing collection.
+    </action>
+    <action issue="COLLECTIONS-272" dev="tn" type="add" due-to="Chaitanya Mutyala">
+      Added serialization support for "FixedOrderComparator" and "TransformingComparator".
+    </action>
+    <action issue="COLLECTIONS-266" dev="bayard" type="fix" due-to="Joerg Schaible">
+      "MultiKey" will now be correctly serialized/de-serialized.
+    </action>
+    <action issue="COLLECTIONS-265" dev="bayard" type="update" due-to="David Saff">
+      "TreeBag" will now only accept "Comparable" objects as input when used with natural ordering.
+    </action>
+    <action issue="COLLECTIONS-263" dev="tn" type="add" due-to="John Hunsley">
+      Added methods "MapUtils#populateMap(MultiMap, ...)" to support also "MultiMap" instances
+      as input.
+    </action>
+    <action issue="COLLECTIONS-262" dev="bayard" type="fix" due-to="Lisen Mu">
+      Fixed javadoc for methods "firstKey()" and "lastKey()" in class "AbstractLinkedMap".
+    </action>
+    <action issue="COLLECTIONS-261" dev="bayard" type="fix" due-to="ori">
+      "Flat3Map#remove(Object)" will now return the correct value mapped to the removed key
+      if the size of the map is less or equal 3.
+    </action>
+    <action issue="COLLECTIONS-260" dev="mbenson" type="add" due-to="Stephen Kestle">
+      Added constructor "TransformingComparator(Transformer)".
+    </action>
+    <action issue="COLLECTIONS-258" dev="tn" type="add" due-to="Nathan Blomquist">
+      Added "DualLinkedHashBidiMap" bidi map implementation.
+    </action>
+    <action issue="COLLECTIONS-255" dev="mbenson" type="fix" due-to="Henri Yandell">
+      Removed unused variables in "TreeBidiMap".
+    </action>
+    <action issue="COLLECTIONS-251,COLLECTIONS-321" dev="mbenson" type="update" due-to="Stephen Kestle">
+      The static factory methods have been renamed from "getInstance()" to a camel-case
+      version of the class name, e.g. "truePredicate()" for class "TruePredicate".
+    </action>
+    <action issue="COLLECTIONS-249" dev="bayard" type="fix" due-to="Joe Kelly">
+      "SetUniqueList.addAll(int, Collection)" now correctly add the collection at the
+      provided index.
+    </action>
+    <action issue="COLLECTIONS-242" dev="skestle" type="add">
+      Added "Equator" interface.
+    </action>
+    <action issue="COLLECTIONS-241" dev="brentworden" type="add" due-to="Elifarley Callado Coelho">
+      Added "PassiveExpiringMap" map decorator.
+    </action>
+    <action issue="COLLECTIONS-240" dev="bayard" type="update" due-to="Wouter de Vaal">
+      "MultiValueMap" is now serializable.
+    </action>
+    <action issue="COLLECTIONS-237" dev="tn" type="add" due-to="Nils Kaiser, Alan Mehlo">
+      Added method "MultiValueMap#iterator()" to return a flattened version of
+      "entrySet().iterator()". Clarified javadoc for "entrySet()" that the returned Entry
+      objects are unflattened, i.e. the Entry object for a given key contains all values
+      mapped to this key.
+    </action>
+    <action issue="COLLECTIONS-235" dev="bayard" type="add" due-to="Nathan Egge">
+      Added method "ListUtils#indexOf(List, Predicate)".
+    </action>
+    <action issue="COLLECTIONS-232" dev="bayard" type="fix" due-to="Mark Hindess">
+      Fixed several unit tests which were using parameters to "assertEquals(...)" in wrong order.
+    </action>
+    <action issue="COLLECTIONS-231" dev="tn" type="update" due-to="Torsten Curdt">
+      Return concrete class in static factory methods instead of base class interface
+      (except for Unmodifiable decorators).
+    </action>
+    <action issue="COLLECTIONS-230,COLLECTIONS-297,COLLECTIONS-318" dev="bayard" type="update" due-to="Stepan Koltsov,sebb">
+      "CollectionUtils#size(Collection)" now returns 0 when called with null as input.
+    </action>
+    <action issue="COLLECTIONS-229" dev="scolebourne" type="remove">
+      Removed deprecated classes and methods.
+    </action>
+    <action issue="COLLECTIONS-228" dev="scolebourne" type="fix">
+      "MultiValueMap#put(Object, Object)" and "MultiValueMap#putAll(Object, Collection)"
+      now correctly return if the map has changed by this operation.
+    </action>
+    <action issue="COLLECTIONS-226" dev="bayard" type="add" due-to="Vasily Ivanov">
+      Added method "ListOrderedMap#putAll(int, Map)".
+    </action>
+    <action issue="COLLECTIONS-225" dev="tn" type="add" due-to="Sam Berlin, Roger Kapsi">
+      Added new "Trie" interface with a first concrete implementation "PatriciaTrie"
+      together with decorators "Unmodifiable" and "Synchronized".
+    </action>
+    <action issue="COLLECTIONS-223" dev="bayard" type="update" due-to="Vasily Ivanov">
+      "CollectionUtils#addAll(...)" methods now return if the collection has been changed
+      by this operation.
+    </action>
+    <action issue="COLLECTIONS-221" dev="bayard" type="update" due-to="Pal Denes">
+      "CompositeCollection", "CompositeMap" and "CompositeSet" are now serializable.
+    </action>
+    <action issue="COLLECTIONS-219" dev="scolebourne" type="fix" due-to="Tom Leccese">
+      "CollectionUtils#removeAll" wrongly called "ListUtils#retainAll".
+    </action>
+    <action issue="COLLECTIONS-218" dev="skestle" type="update">
+      The "CollectionUtils#select(Collection, Predicate, Collection)" method will now
+      return the output collection.
+    </action>
+    <action issue="COLLECTIONS-217" dev="scolebourne" type="fix" due-to="Matt Bishop">
+      Calling "setValue(Object)" on any Entry returned by a "Flat3Map" will now
+      correctly set the value for the current entry.
+    </action>
+    <action issue="COLLECTIONS-216" dev="scolebourne" type="fix" due-to="Hendrik Maryns">
+      "MultiKey#toString()" will now use "Arrays#toString(List)".
+    </action>
+    <action issue="COLLECTIONS-213" dev="brentworden" type="add" due-to="Dusan Chromy">
+      Added support for resettable iterators in "IteratorIterable".
+    </action>
+    <action issue="COLLECTIONS-194" dev="bayard" type="add" due-to="Dave Meikle">
+      Added methods "MapUtils#populateMap(Map, Iterable, Transformer, ...)".
+    </action>
+    <action issue="COLLECTIONS-182" dev="mbenson" type="update" due-to="Jim Cakalic">
+      "CollectionUtils#forAllDo(Collection, Closure)" now returns the provided closure.
+    </action>
+    <action issue="COLLECTIONS-110,COLLECTIONS-243,COLLECTIONS-245,COLLECTIONS-247,
+                   COLLECTIONS-253,COLLECTIONS-273,COLLECTIONS-282" dev="multiple" type="update">
+      Make generic versions of all classes in collections.
+    </action>
+    <action issue="COLLECTIONS-8" dev="brentworden" type="add" due-to="Rune Peter Bjørnstad">
+      Added class "ComparatorPredicate".
+    </action>
+  </release>
+  <release version="3.2.2" date="2015-11-15" description="This is a security and bugfix release.">
+    <action issue="COLLECTIONS-580" dev="tn" type="update">
+      Serialization support for unsafe classes in the functor package is disabled
+      by default as this can be exploited for remote code execution attacks.
+      To re-enable the feature the system property "org.apache.commons.collections.enableUnsafeSerialization"
+      needs to be set to "true".
+      Classes considered to be unsafe are: CloneTransformer, ForClosure, InstantiateFactory,
+      InstantiateTransformer, InvokerTransformer, PrototypeCloneFactory,
+      PrototypeSerializationFactory, WhileClosure.
+    </action>
+    <action issue="COLLECTIONS-538" dev="tn" type="fix" due-to="Trejkaz">
+      "ExtendedProperties" will now use a privileged action to access the
+      "file.separator" system property. In case the class does not have
+      permission to read system properties, the "File#separator" field will
+      be used instead.
+    </action>
+    <action issue="COLLECTIONS-447" dev="tn" type="fix" due-to="Jeffrey Barnes">
+      Tree traversal with a TreeListIterator will not be affected anymore by
+      the removal of an element directly after a call to previous().
+    </action>
+    <action issue="COLLECTIONS-444" dev="tn" type="fix" due-to="Thomas Vahrst, John Vasileff">
+      SetUniqueList.set(int, Object) now works correctly if the object to be inserted
+      is already placed at the given position.
+    </action>
+    <action issue="COLLECTIONS-350" dev="bayard" type="fix" due-to="Michael Akerman">
+      Removed debug output in "MapUtils#getNumber(Map)".
+    </action>
+    <action issue="COLLECTIONS-335" dev="jochen" type="fix" due-to="sebb">
+      Fixed cache assignment for "TreeBidiMap#entrySet".
+    </action>
+    <action issue="COLLECTIONS-334" dev="jochen" type="fix" due-to="sebb">
+      Synchronized access to lock in "StaticBucketMap#size()".
+    </action>
+    <action issue="COLLECTIONS-307" dev="bayard" type="fix" due-to="Christian Semrau">
+      "SetUniqueList#subList()#contains(Object)" will now correctly check the subList
+      rather than the parent list.
+    </action>
+    <action issue="COLLECTIONS-304" dev="bayard" type="fix" due-to="Rafał Figas,Bjorn Townsend">
+      "SetUniqueList#set(int, Object)" will now correctly enforce the uniqueness constraint.
+    </action>
+    <action issue="COLLECTIONS-294" dev="bayard" type="fix" due-to="Benjamin Bentmann">
+      "CaseInsensitiveMap" will now convert input strings to lower-case in a
+      locale-independent manner.
+    </action>
+    <action issue="COLLECTIONS-266" dev="bayard" type="fix" due-to="Joerg Schaible">
+      "MultiKey" will now be correctly serialized/de-serialized.
+    </action>
+    <action issue="COLLECTIONS-261" dev="bayard" type="fix" due-to="ori">
+      "Flat3Map#remove(Object)" will now return the correct value mapped to the removed key
+      if the size of the map is less or equal 3.
+    </action>
+    <action issue="COLLECTIONS-249" dev="bayard" type="fix" due-to="Joe Kelly">
+      "SetUniqueList.addAll(int, Collection)" now correctly add the collection at the
+      provided index.
+    </action>
+    <action issue="COLLECTIONS-228" dev="scolebourne" type="fix">
+      "MultiValueMap#put(Object, Object)" and "MultiValueMap#putAll(Object, Collection)"
+      now correctly return if the map has changed by this operation.
+    </action>
+    <action issue="COLLECTIONS-219" dev="scolebourne" type="fix" due-to="Tom Leccese">
+      "CollectionUtils#removeAll" wrongly called "ListUtils#retainAll".
+    </action>
+    <action issue="COLLECTIONS-217" dev="scolebourne" type="fix" due-to="Matt Bishop">
+      Calling "setValue(Object)" on any Entry returned by a "Flat3Map" will now
+      correctly set the value for the current entry.
+    </action>
+  </release>
+  </body>
+</document>
diff --git a/src/main/java/org/apache/commons/collections4/MapUtils.java b/src/main/java/org/apache/commons/collections4/MapUtils.java
index 08727a0..c5a1516 100644
--- a/src/main/java/org/apache/commons/collections4/MapUtils.java
+++ b/src/main/java/org/apache/commons/collections4/MapUtils.java
@@ -32,6 +32,8 @@ import java.util.Properties;
 import java.util.ResourceBundle;
 import java.util.SortedMap;
 import java.util.TreeMap;
+import java.util.function.BiFunction;
+import java.util.function.Function;
 
 import org.apache.commons.collections4.map.AbstractMapDecorator;
 import org.apache.commons.collections4.map.AbstractSortedMapDecorator;
@@ -49,31 +51,29 @@ import org.apache.commons.collections4.map.UnmodifiableMap;
 import org.apache.commons.collections4.map.UnmodifiableSortedMap;
 
 /**
- * Provides utility methods and decorators for
- * {@link Map} and {@link SortedMap} instances.
+ * Provides utility methods and decorators for {@link Map} and {@link SortedMap} instances.
  * <p>
- * It contains various type safe methods
- * as well as other useful features like deep copying.
+ * It contains various type safe methods as well as other useful features like deep copying.
  * </p>
  * <p>
  * It also provides the following decorators:
  * </p>
  *
- *  <ul>
- *  <li>{@link #fixedSizeMap(Map)}
- *  <li>{@link #fixedSizeSortedMap(SortedMap)}
- *  <li>{@link #lazyMap(Map,Factory)}
- *  <li>{@link #lazyMap(Map,Transformer)}
- *  <li>{@link #lazySortedMap(SortedMap,Factory)}
- *  <li>{@link #lazySortedMap(SortedMap,Transformer)}
- *  <li>{@link #predicatedMap(Map,Predicate,Predicate)}
- *  <li>{@link #predicatedSortedMap(SortedMap,Predicate,Predicate)}
- *  <li>{@link #transformedMap(Map, Transformer, Transformer)}
- *  <li>{@link #transformedSortedMap(SortedMap, Transformer, Transformer)}
- *  <li>{@link #multiValueMap( Map )}
- *  <li>{@link #multiValueMap( Map, Class )}
- *  <li>{@link #multiValueMap( Map, Factory )}
- *  </ul>
+ * <ul>
+ * <li>{@link #fixedSizeMap(Map)}
+ * <li>{@link #fixedSizeSortedMap(SortedMap)}
+ * <li>{@link #lazyMap(Map,Factory)}
+ * <li>{@link #lazyMap(Map,Transformer)}
+ * <li>{@link #lazySortedMap(SortedMap,Factory)}
+ * <li>{@link #lazySortedMap(SortedMap,Transformer)}
+ * <li>{@link #predicatedMap(Map,Predicate,Predicate)}
+ * <li>{@link #predicatedSortedMap(SortedMap,Predicate,Predicate)}
+ * <li>{@link #transformedMap(Map, Transformer, Transformer)}
+ * <li>{@link #transformedSortedMap(SortedMap, Transformer, Transformer)}
+ * <li>{@link #multiValueMap( Map )}
+ * <li>{@link #multiValueMap( Map, Class )}
+ * <li>{@link #multiValueMap( Map, Factory )}
+ * </ul>
  *
  * @since 1.0
  */
@@ -81,12 +81,10 @@ import org.apache.commons.collections4.map.UnmodifiableSortedMap;
 public class MapUtils {
 
     /**
-     * An empty unmodifiable sorted map.
-     * This is not provided in the JDK.
+     * An empty unmodifiable sorted map. This is not provided in the JDK.
      */
     @SuppressWarnings("rawtypes")
-    public static final SortedMap EMPTY_SORTED_MAP =
-            UnmodifiableSortedMap.unmodifiableSortedMap(new TreeMap<>());
+    public static final SortedMap EMPTY_SORTED_MAP = UnmodifiableSortedMap.unmodifiableSortedMap(new TreeMap<>());
 
     /**
      * String used to indent the verbose and debug Map prints.
@@ -94,61 +92,134 @@ public class MapUtils {
     private static final String INDENT_STRING = "    ";
 
     /**
-     * <code>MapUtils</code> should not normally be instantiated.
+     * Applies the {@code getFunction} and returns its result if non-null, if null returns the result of applying the
+     * default function.
+     *
+     * @param <K> The key type.
+     * @param <R> The result type.
+     * @param map The map to query.
+     * @param key The key into the map.
+     * @param getFunction The get function.
+     * @param defaultFunction The function to provide a default value.
+     * @return The result of applying a function.
      */
-    private MapUtils() {}
+    private static <K, R> R applyDefaultFunction(final Map<? super K, ?> map, final K key,
+            final BiFunction<Map<? super K, ?>, K, R> getFunction, final Function<K, R> defaultFunction) {
+        return applyDefaultFunction(map, key, getFunction, defaultFunction, null);
+    }
 
-    // Type safe getters
-    //-------------------------------------------------------------------------
     /**
-     * Gets from a Map in a null-safe manner.
+     * Applies the {@code getFunction} and returns its result if non-null, if null returns the result of applying the
+     * default function.
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map, <code>null</code> if null map input
+     * @param <K> The key type.
+     * @param <R> The result type.
+     * @param map The map to query.
+     * @param key The key into the map.
+     * @param getFunction The get function.
+     * @param defaultFunction The function to provide a default value.
+     * @param defaultValue The default value.
+     * @return The result of applying a function.
      */
-    public static <K, V> V getObject(final Map<? super K, V> map, final K key) {
-        if (map != null) {
-            return map.get(key);
+    private static <K, R> R applyDefaultFunction(final Map<? super K, ?> map, final K key,
+            final BiFunction<Map<? super K, ?>, K, R> getFunction, final Function<K, R> defaultFunction,
+            final R defaultValue) {
+        R value = map != null && getFunction != null ? getFunction.apply(map, key) : null;
+        if (value == null) {
+            value = defaultFunction != null ? defaultFunction.apply(key) : null;
         }
-        return null;
+        return value != null ? value : defaultValue;
     }
 
     /**
-     * Gets a String from a Map in a null-safe manner.
+     * Applies the {@code getFunction} and returns its result if non-null, if null returns the {@code defaultValue}.
+     *
+     * @param <K> The key type.
+     * @param <R> The result type.
+     * @param map The map to query.
+     * @param key The key into the map.
+     * @param getFunction The get function.
+     * @param defaultValue The default value.
+     * @return The result of applying a function.
+     */
+    private static <K, R> R applyDefaultValue(final Map<? super K, ?> map, final K key,
+            final BiFunction<Map<? super K, ?>, K, R> getFunction, final R defaultValue) {
+        final R value = map != null && getFunction != null ? getFunction.apply(map, key) : null;
+        return value == null ? defaultValue : value;
+    }
+
+    /**
+     * Prints the given map with nice line breaks.
      * <p>
-     * The String is obtained via <code>toString</code>.
+     * This method prints a nicely formatted String describing the Map. Each map entry will be printed with key, value
+     * and value classname. When the value is a Map, recursive behaviour occurs.
+     * </p>
+     * <p>
+     * This method is NOT thread-safe in any special way. You must manually synchronize on either this class or the
+     * stream as required.
+     * </p>
+     *
+     * @param out the stream to print to, must not be null
+     * @param label The label to be used, may be <code>null</code>. If <code>null</code>, the label is not output. It
+     *        typically represents the name of the property in a bean or similar.
+     * @param map The map to print, may be <code>null</code>. If <code>null</code>, the text 'null' is output.
+     * @throws NullPointerException if the stream is <code>null</code>
+     */
+    public static void debugPrint(final PrintStream out, final Object label, final Map<?, ?> map) {
+        verbosePrintInternal(out, label, map, new ArrayDeque<Map<?, ?>>(), true);
+    }
+
+    /**
+     * Returns an immutable empty map if the argument is <code>null</code>, or the argument itself otherwise.
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a String, <code>null</code> if null map input
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map, possibly <code>null</code>
+     * @return an empty map if the argument is <code>null</code>
      */
-    public static <K> String getString(final Map<? super K, ?> map, final K key) {
-        if (map != null) {
-            final Object answer = map.get(key);
-            if (answer != null) {
-                return answer.toString();
-            }
-        }
-        return null;
+    public static <K, V> Map<K, V> emptyIfNull(final Map<K, V> map) {
+        return map == null ? Collections.<K, V>emptyMap() : map;
+    }
+
+    /**
+     * Returns a fixed-sized map backed by the given map. Elements may not be added or removed from the returned map,
+     * but existing elements can be changed (for instance, via the {@link Map#put(Object,Object)} method).
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map whose size to fix, must not be null
+     * @return a fixed-size map backed by that map
+     * @throws NullPointerException if the Map is null
+     */
+    public static <K, V> IterableMap<K, V> fixedSizeMap(final Map<K, V> map) {
+        return FixedSizeMap.fixedSizeMap(map);
+    }
+
+    /**
+     * Returns a fixed-sized sorted map backed by the given sorted map. Elements may not be added or removed from the
+     * returned map, but existing elements can be changed (for instance, via the {@link Map#put(Object,Object)} method).
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map whose size to fix, must not be null
+     * @return a fixed-size map backed by that map
+     * @throws NullPointerException if the SortedMap is null
+     */
+    public static <K, V> SortedMap<K, V> fixedSizeSortedMap(final SortedMap<K, V> map) {
+        return FixedSizeSortedMap.fixedSizeSortedMap(map);
     }
 
     /**
      * Gets a Boolean from a Map in a null-safe manner.
      * <p>
-     * If the value is a <code>Boolean</code> it is returned directly.
-     * If the value is a <code>String</code> and it equals 'true' ignoring case
-     * then <code>true</code> is returned, otherwise <code>false</code>.
-     * If the value is a <code>Number</code> an integer zero value returns
-     * <code>false</code> and non-zero returns <code>true</code>.
+     * If the value is a <code>Boolean</code> it is returned directly. If the value is a <code>String</code> and it
+     * equals 'true' ignoring case then <code>true</code> is returned, otherwise <code>false</code>. If the value is a
+     * <code>Number</code> an integer zero value returns <code>false</code> and non-zero returns <code>true</code>.
      * Otherwise, <code>null</code> is returned.
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
      * @return the value in the Map as a Boolean, <code>null</code> if null map input
      */
     public static <K> Boolean getBoolean(final Map<? super K, ?> map, final K key) {
@@ -171,47 +242,110 @@ public class MapUtils {
     }
 
     /**
-     * Gets a Number from a Map in a null-safe manner.
+     * Looks up the given key in the given map, converting the result into a boolean, using the default value if the
+     * conversion fails.
+     *
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultValue what to return if the value is null or if the conversion fails
+     * @return the value in the map as a boolean, or defaultValue if the original value is null, the map is null or the
+     *         boolean conversion fails
+     */
+    public static <K> Boolean getBoolean(final Map<? super K, ?> map, final K key, final Boolean defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getBoolean, defaultValue);
+    }
+
+    /**
+     * Looks up the given key in the given map, converting the result into a boolean, using the defaultFunction to
+     * produce the default value if the conversion fails.
+     *
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultFunction what to produce the default value if the value is null or if the conversion fails
+     * @return the value in the map as a boolean, or defaultValue produced by the defaultFunction if the original value
+     *         is null, the map is null or the boolean conversion fails
+     * @since 4.5
+     */
+    public static <K> Boolean getBoolean(final Map<? super K, ?> map, final K key,
+            final Function<K, Boolean> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getBoolean, defaultFunction);
+    }
+
+    // Type safe primitive getters
+    // -------------------------------------------------------------------------
+    /**
+     * Gets a boolean from a Map in a null-safe manner.
      * <p>
-     * If the value is a <code>Number</code> it is returned directly.
-     * If the value is a <code>String</code> it is converted using
-     * {@link NumberFormat#parse(String)} on the system default formatter
-     * returning <code>null</code> if the conversion fails.
-     * Otherwise, <code>null</code> is returned.
+     * If the value is a <code>Boolean</code> its value is returned. If the value is a <code>String</code> and it equals
+     * 'true' ignoring case then <code>true</code> is returned, otherwise <code>false</code>. If the value is a
+     * <code>Number</code> an integer zero value returns <code>false</code> and non-zero returns <code>true</code>.
+     * Otherwise, <code>false</code> is returned.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a Number, <code>null</code> if null map input
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a Boolean, <code>false</code> if null map input
      */
-    public static <K> Number getNumber(final Map<? super K, ?> map, final K key) {
-        if (map != null) {
-            final Object answer = map.get(key);
-            if (answer != null) {
-                if (answer instanceof Number) {
-                    return (Number) answer;
-                }
-                if (answer instanceof String) {
-                    try {
-                        final String text = (String) answer;
-                        return NumberFormat.getInstance().parse(text);
-                    } catch (final ParseException e) { // NOPMD
-                        // failure means null is returned
-                    }
-                }
-            }
-        }
-        return null;
+    public static <K> boolean getBooleanValue(final Map<? super K, ?> map, final K key) {
+        return Boolean.TRUE.equals(getBoolean(map, key));
+    }
+
+    // Type safe primitive getters with default values
+    // -------------------------------------------------------------------------
+    /**
+     * Gets a boolean from a Map in a null-safe manner, using the default value if the conversion fails.
+     * <p>
+     * If the value is a <code>Boolean</code> its value is returned. If the value is a <code>String</code> and it equals
+     * 'true' ignoring case then <code>true</code> is returned, otherwise <code>false</code>. If the value is a
+     * <code>Number</code> an integer zero value returns <code>false</code> and non-zero returns <code>true</code>.
+     * Otherwise, <code>defaultValue</code> is returned.
+     * </p>
+     *
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultValue return if the value is null or if the conversion fails
+     * @return the value in the Map as a Boolean, <code>defaultValue</code> if null map input
+     */
+    public static <K> boolean getBooleanValue(final Map<? super K, ?> map, final K key, final boolean defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getBoolean, defaultValue).booleanValue();
+    }
+
+    /**
+     * Gets a boolean from a Map in a null-safe manner, using the default value produced by the defaultFunction if the
+     * conversion fails.
+     * <p>
+     * If the value is a <code>Boolean</code> its value is returned. If the value is a <code>String</code> and it equals
+     * 'true' ignoring case then <code>true</code> is returned, otherwise <code>false</code>. If the value is a
+     * <code>Number</code> an integer zero value returns <code>false</code> and non-zero returns <code>true</code>.
+     * Otherwise, defaultValue produced by the <code>defaultFunction</code> is returned.
+     * </p>
+     *
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultFunction produce the default value to return if the value is null or if the conversion fails
+     * @return the value in the Map as a Boolean, default value produced by the <code>defaultFunction</code> if null map
+     *         input
+     * @since 4.5
+     */
+    public static <K> boolean getBooleanValue(final Map<? super K, ?> map, final K key,
+            final Function<K, Boolean> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getBoolean, defaultFunction, false).booleanValue();
     }
 
     /**
      * Gets a Byte from a Map in a null-safe manner.
      * <p>
      * The Byte is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
      * @return the value in the Map as a Byte, <code>null</code> if null map input
      */
     public static <K> Byte getByte(final Map<? super K, ?> map, final K key) {
@@ -226,97 +360,96 @@ public class MapUtils {
     }
 
     /**
-     * Gets a Short from a Map in a null-safe manner.
-     * <p>
-     * The Short is obtained from the results of {@link #getNumber(Map,Object)}.
+     * Looks up the given key in the given map, converting the result into a byte, using the default value if the
+     * conversion fails.
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a Short, <code>null</code> if null map input
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultValue what to return if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue if the original value is null, the map is null or the
+     *         number conversion fails
      */
-    public static <K> Short getShort(final Map<? super K, ?> map, final K key) {
-        final Number answer = getNumber(map, key);
-        if (answer == null) {
-            return null;
-        }
-        if (answer instanceof Short) {
-            return (Short) answer;
-        }
-        return Short.valueOf(answer.shortValue());
+    public static <K> Byte getByte(final Map<? super K, ?> map, final K key, final Byte defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getByte, defaultValue);
     }
 
     /**
-     * Gets a Integer from a Map in a null-safe manner.
+     * Looks up the given key in the given map, converting the result into a byte, using the defaultFunction to produce
+     * the default value if the conversion fails.
+     *
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultFunction what to produce the default value if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue produced by the defaultFunction if the original value
+     *         is null, the map is null or the number conversion fails
+     * @since 4.5
+     */
+    public static <K> Byte getByte(final Map<? super K, ?> map, final K key, final Function<K, Byte> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getByte, defaultFunction);
+    }
+
+    /**
+     * Gets a byte from a Map in a null-safe manner.
      * <p>
-     * The Integer is obtained from the results of {@link #getNumber(Map,Object)}.
+     * The byte is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a Integer, <code>null</code> if null map input
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a byte, <code>0</code> if null map input
      */
-    public static <K> Integer getInteger(final Map<? super K, ?> map, final K key) {
-        final Number answer = getNumber(map, key);
-        if (answer == null) {
-            return null;
-        }
-        if (answer instanceof Integer) {
-            return (Integer) answer;
-        }
-        return Integer.valueOf(answer.intValue());
+    public static <K> byte getByteValue(final Map<? super K, ?> map, final K key) {
+        return applyDefaultValue(map, key, MapUtils::getByte, 0).byteValue();
     }
 
     /**
-     * Gets a Long from a Map in a null-safe manner.
+     * Gets a byte from a Map in a null-safe manner, using the default value if the conversion fails.
      * <p>
-     * The Long is obtained from the results of {@link #getNumber(Map,Object)}.
+     * The byte is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a Long, <code>null</code> if null map input
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultValue return if the value is null or if the conversion fails
+     * @return the value in the Map as a byte, <code>defaultValue</code> if null map input
      */
-    public static <K> Long getLong(final Map<? super K, ?> map, final K key) {
-        final Number answer = getNumber(map, key);
-        if (answer == null) {
-            return null;
-        }
-        if (answer instanceof Long) {
-            return (Long) answer;
-        }
-        return Long.valueOf(answer.longValue());
+    public static <K> byte getByteValue(final Map<? super K, ?> map, final K key, final byte defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getByte, defaultValue).byteValue();
     }
 
     /**
-     * Gets a Float from a Map in a null-safe manner.
+     * Gets a byte from a Map in a null-safe manner, using the default value produced by the defaultFunction if the
+     * conversion fails.
      * <p>
-     * The Float is obtained from the results of {@link #getNumber(Map,Object)}.
+     * The byte is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a Float, <code>null</code> if null map input
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultFunction produce the default value to return if the value is null or if the conversion fails
+     * @return the value in the Map as a byte, default value produced by the <code>defaultFunction</code> if null map
+     *         input
+     * @since 4.5
      */
-    public static <K> Float getFloat(final Map<? super K, ?> map, final K key) {
-        final Number answer = getNumber(map, key);
-        if (answer == null) {
-            return null;
-        }
-        if (answer instanceof Float) {
-            return (Float) answer;
-        }
-        return Float.valueOf(answer.floatValue());
+    public static <K> byte getByteValue(final Map<? super K, ?> map, final K key,
+            final Function<K, Byte> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getByte, defaultFunction, (byte) 0).byteValue();
     }
 
     /**
      * Gets a Double from a Map in a null-safe manner.
      * <p>
      * The Double is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
      * @return the value in the Map as a Double, <code>null</code> if null map input
      */
     public static <K> Double getDouble(final Map<? super K, ?> map, final K key) {
@@ -331,1024 +464,814 @@ public class MapUtils {
     }
 
     /**
-     * Gets a Map from a Map in a null-safe manner.
-     * <p>
-     * If the value returned from the specified map is not a Map then
-     * <code>null</code> is returned.
+     * Looks up the given key in the given map, converting the result into a double, using the default value if the
+     * conversion fails.
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a Map, <code>null</code> if null map input
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultValue what to return if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue if the original value is null, the map is null or the
+     *         number conversion fails
      */
-    public static <K> Map<?, ?> getMap(final Map<? super K, ?> map, final K key) {
-        if (map != null) {
-            final Object answer = map.get(key);
-            if (answer != null && answer instanceof Map) {
-                return (Map<?, ?>) answer;
-            }
-        }
-        return null;
+    public static <K> Double getDouble(final Map<? super K, ?> map, final K key, final Double defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getDouble, defaultValue);
     }
 
-    // Type safe getters with default values
-    //-------------------------------------------------------------------------
     /**
-     * Looks up the given key in the given map, converting null into the
-     * given default value.
+     * Looks up the given key in the given map, converting the result into a double, using the defaultFunction to
+     * produce the default value if the conversion fails.
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map whose value to look up
-     * @param key  the key of the value to look up in that map
-     * @param defaultValue  what to return if the value is null
-     * @return  the value in the map, or defaultValue if the original value
-     *   is null or the map is null
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultFunction what to produce the default value if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue produced by the defaultFunction if the original value
+     *         is null, the map is null or the number conversion fails
+     * @since 4.5
      */
-    public static <K, V> V getObject(final Map<K, V> map, final K key, final V defaultValue) {
-        if (map != null) {
-            final V answer = map.get(key);
-            if (answer != null) {
-                return answer;
-            }
-        }
-        return defaultValue;
+    public static <K> Double getDouble(final Map<? super K, ?> map, final K key,
+            final Function<K, Double> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getDouble, defaultFunction);
     }
 
     /**
-     * Looks up the given key in the given map, converting the result into
-     * a string, using the default value if the conversion fails.
+     * Gets a double from a Map in a null-safe manner.
+     * <p>
+     * The double is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map whose value to look up
-     * @param key  the key of the value to look up in that map
-     * @param defaultValue  what to return if the value is null or if the
-     *   conversion fails
-     * @return  the value in the map as a string, or defaultValue if the
-     *   original value is null, the map is null or the string conversion fails
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a double, <code>0.0</code> if null map input
      */
-    public static <K> String getString(final Map<? super K, ?> map, final K key, final String defaultValue) {
-        String answer = getString(map, key);
-        if (answer == null) {
-            answer = defaultValue;
-        }
-        return answer;
+    public static <K> double getDoubleValue(final Map<? super K, ?> map, final K key) {
+        return applyDefaultValue(map, key, MapUtils::getDouble, 0d).doubleValue();
     }
 
     /**
-     * Looks up the given key in the given map, converting the result into
-     * a boolean, using the default value if the conversion fails.
+     * Gets a double from a Map in a null-safe manner, using the default value if the conversion fails.
+     * <p>
+     * The double is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map whose value to look up
-     * @param key  the key of the value to look up in that map
-     * @param defaultValue  what to return if the value is null or if the
-     *   conversion fails
-     * @return  the value in the map as a boolean, or defaultValue if the
-     *   original value is null, the map is null or the boolean conversion fails
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultValue return if the value is null or if the conversion fails
+     * @return the value in the Map as a double, <code>defaultValue</code> if null map input
      */
-    public static <K> Boolean getBoolean(final Map<? super K, ?> map, final K key, final Boolean defaultValue) {
-        Boolean answer = getBoolean(map, key);
-        if (answer == null) {
-            answer = defaultValue;
-        }
-        return answer;
+    public static <K> double getDoubleValue(final Map<? super K, ?> map, final K key, final double defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getDouble, defaultValue).doubleValue();
     }
 
     /**
-     * Looks up the given key in the given map, converting the result into
-     * a number, using the default value if the conversion fails.
+     * Gets a double from a Map in a null-safe manner, using the default value produced by the defaultFunction if the
+     * conversion fails.
+     * <p>
+     * The double is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map whose value to look up
-     * @param key  the key of the value to look up in that map
-     * @param defaultValue  what to return if the value is null or if the
-     *   conversion fails
-     * @return  the value in the map as a number, or defaultValue if the
-     *   original value is null, the map is null or the number conversion fails
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultFunction produce the default value to return if the value is null or if the conversion fails
+     * @return the value in the Map as a double, default value produced by the <code>defaultFunction</code> if null map
+     *         input
+     * @since 4.5
      */
-    public static <K> Number getNumber(final Map<? super K, ?> map, final K key, final Number defaultValue) {
-        Number answer = getNumber(map, key);
-        if (answer == null) {
-            answer = defaultValue;
-        }
-        return answer;
+    public static <K> double getDoubleValue(final Map<? super K, ?> map, final K key,
+            final Function<K, Double> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getDouble, defaultFunction, 0d).doubleValue();
     }
 
     /**
-     * Looks up the given key in the given map, converting the result into
-     * a byte, using the default value if the conversion fails.
+     * Gets a Float from a Map in a null-safe manner.
+     * <p>
+     * The Float is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map whose value to look up
-     * @param key  the key of the value to look up in that map
-     * @param defaultValue  what to return if the value is null or if the
-     *   conversion fails
-     * @return  the value in the map as a number, or defaultValue if the
-     *   original value is null, the map is null or the number conversion fails
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a Float, <code>null</code> if null map input
      */
-    public static <K> Byte getByte(final Map<? super K, ?> map, final K key, final Byte defaultValue) {
-        Byte answer = getByte(map, key);
+    public static <K> Float getFloat(final Map<? super K, ?> map, final K key) {
+        final Number answer = getNumber(map, key);
         if (answer == null) {
-            answer = defaultValue;
+            return null;
         }
-        return answer;
+        if (answer instanceof Float) {
+            return (Float) answer;
+        }
+        return Float.valueOf(answer.floatValue());
     }
 
     /**
-     * Looks up the given key in the given map, converting the result into
-     * a short, using the default value if the conversion fails.
+     * Looks up the given key in the given map, converting the result into a float, using the default value if the
+     * conversion fails.
      *
-     * @param <K>  the key type
-     * @param map  the map whose value to look up
-     * @param key  the key of the value to look up in that map
-     * @param defaultValue  what to return if the value is null or if the
-     *   conversion fails
-     * @return  the value in the map as a number, or defaultValue if the
-     *   original value is null, the map is null or the number conversion fails
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultValue what to return if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue if the original value is null, the map is null or the
+     *         number conversion fails
      */
-    public static <K> Short getShort(final Map<? super K, ?> map, final K key, final Short defaultValue) {
-        Short answer = getShort(map, key);
-        if (answer == null) {
-            answer = defaultValue;
-        }
-        return answer;
+    public static <K> Float getFloat(final Map<? super K, ?> map, final K key, final Float defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getFloat, defaultValue);
     }
 
     /**
-     * Looks up the given key in the given map, converting the result into
-     * an integer, using the default value if the conversion fails.
+     * Looks up the given key in the given map, converting the result into a float, using the defaultFunction to produce
+     * the default value if the conversion fails.
      *
-     * @param <K>  the key type
-     * @param map  the map whose value to look up
-     * @param key  the key of the value to look up in that map
-     * @param defaultValue  what to return if the value is null or if the
-     *   conversion fails
-     * @return  the value in the map as a number, or defaultValue if the
-     *   original value is null, the map is null or the number conversion fails
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultFunction what to produce the default value if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue produced by the defaultFunction if the original value
+     *         is null, the map is null or the number conversion fails
+     * @since 4.5
      */
-    public static <K> Integer getInteger(final Map<? super K, ?> map, final K key, final Integer defaultValue) {
-        Integer answer = getInteger(map, key);
-        if (answer == null) {
-            answer = defaultValue;
-        }
-        return answer;
+    public static <K> Float getFloat(final Map<? super K, ?> map, final K key,
+            final Function<K, Float> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getFloat, defaultFunction);
     }
 
     /**
-     * Looks up the given key in the given map, converting the result into
-     * a long, using the default value if the conversion fails.
+     * Gets a float from a Map in a null-safe manner.
+     * <p>
+     * The float is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map whose value to look up
-     * @param key  the key of the value to look up in that map
-     * @param defaultValue  what to return if the value is null or if the
-     *   conversion fails
-     * @return  the value in the map as a number, or defaultValue if the
-     *   original value is null, the map is null or the number conversion fails
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a float, <code>0.0F</code> if null map input
      */
-    public static <K> Long getLong(final Map<? super K, ?> map, final K key, final Long defaultValue) {
-        Long answer = getLong(map, key);
-        if (answer == null) {
-            answer = defaultValue;
-        }
-        return answer;
+    public static <K> float getFloatValue(final Map<? super K, ?> map, final K key) {
+        return applyDefaultValue(map, key, MapUtils::getFloat, 0f).floatValue();
     }
 
     /**
-     * Looks up the given key in the given map, converting the result into
-     * a float, using the default value if the conversion fails.
+     * Gets a float from a Map in a null-safe manner, using the default value if the conversion fails.
+     * <p>
+     * The float is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map whose value to look up
-     * @param key  the key of the value to look up in that map
-     * @param defaultValue  what to return if the value is null or if the
-     *   conversion fails
-     * @return  the value in the map as a number, or defaultValue if the
-     *   original value is null, the map is null or the number conversion fails
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultValue return if the value is null or if the conversion fails
+     * @return the value in the Map as a float, <code>defaultValue</code> if null map input
      */
-    public static <K> Float getFloat(final Map<? super K, ?> map, final K key, final Float defaultValue) {
-        Float answer = getFloat(map, key);
-        if (answer == null) {
-            answer = defaultValue;
-        }
-        return answer;
+    public static <K> float getFloatValue(final Map<? super K, ?> map, final K key, final float defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getFloat, defaultValue).floatValue();
     }
 
     /**
-     * Looks up the given key in the given map, converting the result into
-     * a double, using the default value if the conversion fails.
+     * Gets a float from a Map in a null-safe manner, using the default value produced by the defaultFunction if the
+     * conversion fails.
+     * <p>
+     * The float is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map whose value to look up
-     * @param key  the key of the value to look up in that map
-     * @param defaultValue  what to return if the value is null or if the
-     *   conversion fails
-     * @return  the value in the map as a number, or defaultValue if the
-     *   original value is null, the map is null or the number conversion fails
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultFunction produce the default value to return if the value is null or if the conversion fails
+     * @return the value in the Map as a float, default value produced by the <code>defaultFunction</code> if null map
+     *         input
+     * @since 4.5
      */
-    public static <K> Double getDouble(final Map<? super K, ?> map, final K key, final Double defaultValue) {
-        Double answer = getDouble(map, key);
-        if (answer == null) {
-            answer = defaultValue;
-        }
-        return answer;
+    public static <K> float getFloatValue(final Map<? super K, ?> map, final K key,
+            final Function<K, Float> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getFloat, defaultFunction, 0f).floatValue();
     }
 
     /**
-     * Looks up the given key in the given map, converting the result into
-     * a map, using the default value if the conversion fails.
+     * Gets a Integer from a Map in a null-safe manner.
+     * <p>
+     * The Integer is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map whose value to look up
-     * @param key  the key of the value to look up in that map
-     * @param defaultValue  what to return if the value is null or if the
-     *   conversion fails
-     * @return  the value in the map as a number, or defaultValue if the
-     *   original value is null, the map is null or the map conversion fails
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a Integer, <code>null</code> if null map input
      */
-    public static <K> Map<?, ?> getMap(final Map<? super K, ?> map, final K key, final Map<?, ?> defaultValue) {
-        Map<?, ?> answer = getMap(map, key);
+    public static <K> Integer getInteger(final Map<? super K, ?> map, final K key) {
+        final Number answer = getNumber(map, key);
         if (answer == null) {
-            answer = defaultValue;
+            return null;
         }
-        return answer;
-    }
-
-    // Type safe primitive getters
-    //-------------------------------------------------------------------------
-    /**
-     * Gets a boolean from a Map in a null-safe manner.
-     * <p>
-     * If the value is a <code>Boolean</code> its value is returned.
-     * If the value is a <code>String</code> and it equals 'true' ignoring case
-     * then <code>true</code> is returned, otherwise <code>false</code>.
-     * If the value is a <code>Number</code> an integer zero value returns
-     * <code>false</code> and non-zero returns <code>true</code>.
-     * Otherwise, <code>false</code> is returned.
-     *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a Boolean, <code>false</code> if null map input
-     */
-    public static <K> boolean getBooleanValue(final Map<? super K, ?> map, final K key) {
-        return Boolean.TRUE.equals(getBoolean(map, key));
+        if (answer instanceof Integer) {
+            return (Integer) answer;
+        }
+        return Integer.valueOf(answer.intValue());
     }
 
     /**
-     * Gets a byte from a Map in a null-safe manner.
-     * <p>
-     * The byte is obtained from the results of {@link #getNumber(Map,Object)}.
+     * Looks up the given key in the given map, converting the result into an integer, using the defaultFunction to
+     * produce the default value if the conversion fails.
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a byte, <code>0</code> if null map input
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultFunction what to produce the default value if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue produced by the defaultFunction if the original value
+     *         is null, the map is null or the number conversion fails
+     * @since 4.5
      */
-    public static <K> byte getByteValue(final Map<? super K, ?> map, final K key) {
-        final Byte byteObject = getByte(map, key);
-        if (byteObject == null) {
-            return 0;
-        }
-        return byteObject.byteValue();
+    public static <K> Integer getInteger(final Map<? super K, ?> map, final K key,
+            final Function<K, Integer> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getInteger, defaultFunction);
     }
 
     /**
-     * Gets a short from a Map in a null-safe manner.
-     * <p>
-     * The short is obtained from the results of {@link #getNumber(Map,Object)}.
+     * Looks up the given key in the given map, converting the result into an integer, using the default value if the
+     * conversion fails.
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a short, <code>0</code> if null map input
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultValue what to return if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue if the original value is null, the map is null or the
+     *         number conversion fails
      */
-    public static <K> short getShortValue(final Map<? super K, ?> map, final K key) {
-        final Short shortObject = getShort(map, key);
-        if (shortObject == null) {
-            return 0;
-        }
-        return shortObject.shortValue();
+    public static <K> Integer getInteger(final Map<? super K, ?> map, final K key, final Integer defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getInteger, defaultValue);
     }
 
     /**
      * Gets an int from a Map in a null-safe manner.
      * <p>
      * The int is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
      * @return the value in the Map as an int, <code>0</code> if null map input
      */
     public static <K> int getIntValue(final Map<? super K, ?> map, final K key) {
-        final Integer integerObject = getInteger(map, key);
-        if (integerObject == null) {
-            return 0;
-        }
-        return integerObject.intValue();
+        return applyDefaultValue(map, key, MapUtils::getInteger, 0).intValue();
     }
 
     /**
-     * Gets a long from a Map in a null-safe manner.
+     * Gets an int from a Map in a null-safe manner, using the default value produced by the defaultFunction if the
+     * conversion fails.
      * <p>
-     * The long is obtained from the results of {@link #getNumber(Map,Object)}.
+     * The int is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a long, <code>0L</code> if null map input
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultFunction produce the default value to return if the value is null or if the conversion fails
+     * @return the value in the Map as an int, default value produced by the <code>defaultFunction</code> if null map
+     *         input
+     * @since 4.5
      */
-    public static <K> long getLongValue(final Map<? super K, ?> map, final K key) {
-        final Long longObject = getLong(map, key);
-        if (longObject == null) {
-            return 0L;
-        }
-        return longObject.longValue();
+    public static <K> int getIntValue(final Map<? super K, ?> map, final K key,
+            final Function<K, Integer> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getInteger, defaultFunction, 0).byteValue();
     }
 
     /**
-     * Gets a float from a Map in a null-safe manner.
+     * Gets an int from a Map in a null-safe manner, using the default value if the conversion fails.
      * <p>
-     * The float is obtained from the results of {@link #getNumber(Map,Object)}.
+     * The int is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a float, <code>0.0F</code> if null map input
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultValue return if the value is null or if the conversion fails
+     * @return the value in the Map as an int, <code>defaultValue</code> if null map input
      */
-    public static <K> float getFloatValue(final Map<? super K, ?> map, final K key) {
-        final Float floatObject = getFloat(map, key);
-        if (floatObject == null) {
-            return 0f;
-        }
-        return floatObject.floatValue();
+    public static <K> int getIntValue(final Map<? super K, ?> map, final K key, final int defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getInteger, defaultValue).intValue();
     }
 
     /**
-     * Gets a double from a Map in a null-safe manner.
+     * Gets a Long from a Map in a null-safe manner.
      * <p>
-     * The double is obtained from the results of {@link #getNumber(Map,Object)}.
+     * The Long is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @return the value in the Map as a double, <code>0.0</code> if null map input
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a Long, <code>null</code> if null map input
      */
-    public static <K> double getDoubleValue(final Map<? super K, ?> map, final K key) {
-        final Double doubleObject = getDouble(map, key);
-        if (doubleObject == null) {
-            return 0d;
+    public static <K> Long getLong(final Map<? super K, ?> map, final K key) {
+        final Number answer = getNumber(map, key);
+        if (answer == null) {
+            return null;
+        }
+        if (answer instanceof Long) {
+            return (Long) answer;
         }
-        return doubleObject.doubleValue();
+        return Long.valueOf(answer.longValue());
     }
 
-    // Type safe primitive getters with default values
-    //-------------------------------------------------------------------------
     /**
-     * Gets a boolean from a Map in a null-safe manner,
-     * using the default value if the conversion fails.
-     * <p>
-     * If the value is a <code>Boolean</code> its value is returned.
-     * If the value is a <code>String</code> and it equals 'true' ignoring case
-     * then <code>true</code> is returned, otherwise <code>false</code>.
-     * If the value is a <code>Number</code> an integer zero value returns
-     * <code>false</code> and non-zero returns <code>true</code>.
-     * Otherwise, <code>defaultValue</code> is returned.
+     * Looks up the given key in the given map, converting the result into a Long, using the defaultFunction to produce
+     * the default value if the conversion fails.
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @param defaultValue  return if the value is null or if the conversion fails
-     * @return the value in the Map as a Boolean, <code>defaultValue</code> if null map input
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultFunction what to produce the default value if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue produced by the defaultFunction if the original value
+     *         is null, the map is null or the number conversion fails
+     * @since 4.5
      */
-    public static <K> boolean getBooleanValue(final Map<? super K, ?> map, final K key, final boolean defaultValue) {
-        final Boolean booleanObject = getBoolean(map, key);
-        if (booleanObject == null) {
-            return defaultValue;
-        }
-        return booleanObject.booleanValue();
+    public static <K> Long getLong(final Map<? super K, ?> map, final K key, final Function<K, Long> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getLong, defaultFunction);
     }
 
     /**
-     * Gets a byte from a Map in a null-safe manner,
-     * using the default value if the conversion fails.
-     * <p>
-     * The byte is obtained from the results of {@link #getNumber(Map,Object)}.
+     * Looks up the given key in the given map, converting the result into a long, using the default value if the
+     * conversion fails.
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @param defaultValue  return if the value is null or if the conversion fails
-     * @return the value in the Map as a byte, <code>defaultValue</code> if null map input
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultValue what to return if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue if the original value is null, the map is null or the
+     *         number conversion fails
      */
-    public static <K> byte getByteValue(final Map<? super K, ?> map, final K key, final byte defaultValue) {
-        final Byte byteObject = getByte(map, key);
-        if (byteObject == null) {
-            return defaultValue;
-        }
-        return byteObject.byteValue();
+    public static <K> Long getLong(final Map<? super K, ?> map, final K key, final Long defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getLong, defaultValue);
     }
 
     /**
-     * Gets a short from a Map in a null-safe manner,
-     * using the default value if the conversion fails.
+     * Gets a long from a Map in a null-safe manner.
      * <p>
-     * The short is obtained from the results of {@link #getNumber(Map,Object)}.
+     * The long is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @param defaultValue  return if the value is null or if the conversion fails
-     * @return the value in the Map as a short, <code>defaultValue</code> if null map input
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a long, <code>0L</code> if null map input
      */
-    public static <K> short getShortValue(final Map<? super K, ?> map, final K key, final short defaultValue) {
-        final Short shortObject = getShort(map, key);
-        if (shortObject == null) {
-            return defaultValue;
-        }
-        return shortObject.shortValue();
+    public static <K> long getLongValue(final Map<? super K, ?> map, final K key) {
+        return applyDefaultValue(map, key, MapUtils::getLong, 0L).longValue();
     }
 
     /**
-     * Gets an int from a Map in a null-safe manner,
-     * using the default value if the conversion fails.
+     * Gets a long from a Map in a null-safe manner, using the default value produced by the defaultFunction if the
+     * conversion fails.
      * <p>
-     * The int is obtained from the results of {@link #getNumber(Map,Object)}.
+     * The long is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @param defaultValue  return if the value is null or if the conversion fails
-     * @return the value in the Map as an int, <code>defaultValue</code> if null map input
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultFunction produce the default value to return if the value is null or if the conversion fails
+     * @return the value in the Map as a long, default value produced by the <code>defaultFunction</code> if null map
+     *         input
+     * @since 4.5
      */
-    public static <K> int getIntValue(final Map<? super K, ?> map, final K key, final int defaultValue) {
-        final Integer integerObject = getInteger(map, key);
-        if (integerObject == null) {
-            return defaultValue;
-        }
-        return integerObject.intValue();
+    public static <K> long getLongValue(final Map<? super K, ?> map, final K key,
+            final Function<K, Long> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getLong, defaultFunction, 0L).byteValue();
     }
 
     /**
-     * Gets a long from a Map in a null-safe manner,
-     * using the default value if the conversion fails.
+     * Gets a long from a Map in a null-safe manner, using the default value if the conversion fails.
      * <p>
      * The long is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @param defaultValue  return if the value is null or if the conversion fails
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultValue return if the value is null or if the conversion fails
      * @return the value in the Map as a long, <code>defaultValue</code> if null map input
      */
     public static <K> long getLongValue(final Map<? super K, ?> map, final K key, final long defaultValue) {
-        final Long longObject = getLong(map, key);
-        if (longObject == null) {
-            return defaultValue;
-        }
-        return longObject.longValue();
+        return applyDefaultValue(map, key, MapUtils::getLong, defaultValue).longValue();
     }
 
     /**
-     * Gets a float from a Map in a null-safe manner,
-     * using the default value if the conversion fails.
+     * Gets a Map from a Map in a null-safe manner.
      * <p>
-     * The float is obtained from the results of {@link #getNumber(Map,Object)}.
+     * If the value returned from the specified map is not a Map then <code>null</code> is returned.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @param defaultValue  return if the value is null or if the conversion fails
-     * @return the value in the Map as a float, <code>defaultValue</code> if null map input
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a Map, <code>null</code> if null map input
      */
-    public static <K> float getFloatValue(final Map<? super K, ?> map, final K key, final float defaultValue) {
-        final Float floatObject = getFloat(map, key);
-        if (floatObject == null) {
-            return defaultValue;
+    public static <K> Map<?, ?> getMap(final Map<? super K, ?> map, final K key) {
+        if (map != null) {
+            final Object answer = map.get(key);
+            if (answer != null && answer instanceof Map) {
+                return (Map<?, ?>) answer;
+            }
         }
-        return floatObject.floatValue();
+        return null;
     }
 
     /**
-     * Gets a double from a Map in a null-safe manner,
-     * using the default value if the conversion fails.
-     * <p>
-     * The double is obtained from the results of {@link #getNumber(Map,Object)}.
+     * Looks up the given key in the given map, converting the result into a map, using the defaultFunction to produce
+     * the default value if the conversion fails.
      *
-     * @param <K>  the key type
-     * @param map  the map to use
-     * @param key  the key to look up
-     * @param defaultValue  return if the value is null or if the conversion fails
-     * @return the value in the Map as a double, <code>defaultValue</code> if null map input
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultFunction what to produce the default value if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue produced by the defaultFunction if the original value
+     *         is null, the map is null or the map conversion fails
+     * @since 4.5
      */
-    public static <K> double getDoubleValue(final Map<? super K, ?> map, final K key, final double defaultValue) {
-        final Double doubleObject = getDouble(map, key);
-        if (doubleObject == null) {
-            return defaultValue;
-        }
-        return doubleObject.doubleValue();
+    public static <K> Map<?, ?> getMap(final Map<? super K, ?> map, final K key,
+            final Function<K, Map<?, ?>> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getMap, defaultFunction);
+    }
+
+    /**
+     * Looks up the given key in the given map, converting the result into a map, using the default value if the
+     * conversion fails.
+     *
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultValue what to return if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue if the original value is null, the map is null or the
+     *         map conversion fails
+     */
+    public static <K> Map<?, ?> getMap(final Map<? super K, ?> map, final K key, final Map<?, ?> defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getMap, defaultValue);
     }
 
-    // Conversion methods
-    //-------------------------------------------------------------------------
     /**
-     * Gets a new Properties object initialised with the values from a Map.
-     * A null input will return an empty properties object.
+     * Gets a Number from a Map in a null-safe manner.
      * <p>
-     * A Properties object may only store non-null keys and values, thus if
-     * the provided map contains either a key or value which is {@code null},
-     * a {@link NullPointerException} will be thrown.
+     * If the value is a <code>Number</code> it is returned directly. If the value is a <code>String</code> it is
+     * converted using {@link NumberFormat#parse(String)} on the system default formatter returning <code>null</code> if
+     * the conversion fails. Otherwise, <code>null</code> is returned.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to convert to a Properties object
-     * @return the properties object
-     * @throws NullPointerException if a key or value in the provided map is {@code null}
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a Number, <code>null</code> if null map input
      */
-    public static <K, V> Properties toProperties(final Map<K, V> map) {
-        final Properties answer = new Properties();
+    public static <K> Number getNumber(final Map<? super K, ?> map, final K key) {
         if (map != null) {
-            for (final Entry<K, V> entry2 : map.entrySet()) {
-                final Map.Entry<?, ?> entry = entry2;
-                final Object key = entry.getKey();
-                final Object value = entry.getValue();
-                answer.put(key, value);
+            final Object answer = map.get(key);
+            if (answer != null) {
+                if (answer instanceof Number) {
+                    return (Number) answer;
+                }
+                if (answer instanceof String) {
+                    try {
+                        final String text = (String) answer;
+                        return NumberFormat.getInstance().parse(text);
+                    } catch (final ParseException e) { // NOPMD
+                        // failure means null is returned
+                    }
+                }
             }
         }
-        return answer;
+        return null;
     }
 
     /**
-     * Creates a new HashMap using data copied from a ResourceBundle.
+     * Looks up the given key in the given map, converting the result into a number, using the defaultFunction to
+     * produce the default value if the conversion fails.
      *
-     * @param resourceBundle  the resource bundle to convert, may not be null
-     * @return the hashmap containing the data
-     * @throws NullPointerException if the bundle is null
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultFunction what to produce the default value if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue produced by the defaultFunction if the original value
+     *         is null, the map is null or the number conversion fails
+     * @since 4.5
      */
-    public static Map<String, Object> toMap(final ResourceBundle resourceBundle) {
-        final Enumeration<String> enumeration = resourceBundle.getKeys();
-        final Map<String, Object> map = new HashMap<>();
-
-        while (enumeration.hasMoreElements()) {
-            final String key = enumeration.nextElement();
-            final Object value = resourceBundle.getObject(key);
-            map.put(key, value);
-        }
-
-        return map;
+    public static <K> Number getNumber(final Map<? super K, ?> map, final K key,
+            final Function<K, Number> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getNumber, defaultFunction);
     }
 
-    // Printing methods
-    //-------------------------------------------------------------------------
     /**
-     * Prints the given map with nice line breaks.
-     * <p>
-     * This method prints a nicely formatted String describing the Map.
-     * Each map entry will be printed with key and value.
-     * When the value is a Map, recursive behaviour occurs.
-     * <p>
-     * This method is NOT thread-safe in any special way. You must manually
-     * synchronize on either this class or the stream as required.
+     * Looks up the given key in the given map, converting the result into a number, using the default value if the
+     * conversion fails.
      *
-     * @param out  the stream to print to, must not be null
-     * @param label  The label to be used, may be <code>null</code>.
-     *  If <code>null</code>, the label is not output.
-     *  It typically represents the name of the property in a bean or similar.
-     * @param map  The map to print, may be <code>null</code>.
-     *  If <code>null</code>, the text 'null' is output.
-     * @throws NullPointerException if the stream is <code>null</code>
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultValue what to return if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue if the original value is null, the map is null or the
+     *         number conversion fails
      */
-    public static void verbosePrint(final PrintStream out, final Object label, final Map<?, ?> map) {
-        verbosePrintInternal(out, label, map, new ArrayDeque<Map<?, ?>>(), false);
+    public static <K> Number getNumber(final Map<? super K, ?> map, final K key, final Number defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getNumber, defaultValue);
     }
 
+    // -------------------------------------------------------------------------
     /**
-     * Prints the given map with nice line breaks.
-     * <p>
-     * This method prints a nicely formatted String describing the Map.
-     * Each map entry will be printed with key, value and value classname.
-     * When the value is a Map, recursive behaviour occurs.
-     * <p>
-     * This method is NOT thread-safe in any special way. You must manually
-     * synchronize on either this class or the stream as required.
+     * Gets from a Map in a null-safe manner.
      *
-     * @param out  the stream to print to, must not be null
-     * @param label  The label to be used, may be <code>null</code>.
-     *   If <code>null</code>, the label is not output.
-     *   It typically represents the name of the property in a bean or similar.
-     * @param map  The map to print, may be <code>null</code>.
-     *   If <code>null</code>, the text 'null' is output.
-     * @throws NullPointerException if the stream is <code>null</code>
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map, <code>null</code> if null map input
      */
-    public static void debugPrint(final PrintStream out, final Object label, final Map<?, ?> map) {
-        verbosePrintInternal(out, label, map, new ArrayDeque<Map<?, ?>>(), true);
+    public static <K, V> V getObject(final Map<? super K, V> map, final K key) {
+        if (map != null) {
+            return map.get(key);
+        }
+        return null;
     }
 
-    // Implementation methods
-    //-------------------------------------------------------------------------
-    /**
-     * Implementation providing functionality for {@link #debugPrint} and for
-     * {@link #verbosePrint}.  This prints the given map with nice line breaks.
-     * If the debug flag is true, it additionally prints the type of the object
-     * value.  If the contents of a map include the map itself, then the text
-     * <em>(this Map)</em> is printed out.  If the contents include a
-     * parent container of the map, the text <em>(ancestor[i] Map)</em> is
-     * printed, where i actually indicates the number of levels which must be
-     * traversed in the sequential list of ancestors (e.g. father, grandfather,
-     * great-grandfather, etc).
-     *
-     * @param out  the stream to print to
-     * @param label  the label to be used, may be <code>null</code>.
-     *   If <code>null</code>, the label is not output.
-     *   It typically represents the name of the property in a bean or similar.
-     * @param map  the map to print, may be <code>null</code>.
-     *   If <code>null</code>, the text 'null' is output
-     * @param lineage  a stack consisting of any maps in which the previous
-     *   argument is contained. This is checked to avoid infinite recursion when
-     *   printing the output
-     * @param debug  flag indicating whether type names should be output.
-     * @throws NullPointerException if the stream is <code>null</code>
+    // -------------------------------------------------------------------------
+    /**
+     * Looks up the given key in the given map, converting null into the given default value.
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultValue what to return if the value is null
+     * @return the value in the map, or defaultValue if the original value is null or the map is null
      */
-    private static void verbosePrintInternal(final PrintStream out, final Object label, final Map<?, ?> map,
-                                             final Deque<Map<?, ?>> lineage, final boolean debug) {
-        printIndent(out, lineage.size());
-
-        if (map == null) {
-            if (label != null) {
-                out.print(label);
-                out.print(" = ");
+    public static <K, V> V getObject(final Map<K, V> map, final K key, final V defaultValue) {
+        if (map != null) {
+            final V answer = map.get(key);
+            if (answer != null) {
+                return answer;
             }
-            out.println("null");
-            return;
-        }
-        if (label != null) {
-            out.print(label);
-            out.println(" = ");
         }
+        return defaultValue;
+    }
 
-        printIndent(out, lineage.size());
-        out.println("{");
-
-        lineage.addLast(map);
-
-        for (final Map.Entry<?, ?> entry : map.entrySet()) {
-            final Object childKey = entry.getKey();
-            final Object childValue = entry.getValue();
-            if (childValue instanceof Map && !lineage.contains(childValue)) {
-                verbosePrintInternal(
-                    out,
-                    childKey == null ? "null" : childKey,
-                    (Map<?, ?>) childValue,
-                    lineage,
-                    debug);
-            } else {
-                printIndent(out, lineage.size());
-                out.print(childKey);
-                out.print(" = ");
-
-                final int lineageIndex =
-                        IterableUtils.indexOf(lineage,
-                                              PredicateUtils.equalPredicate(childValue));
-                if (lineageIndex == -1) {
-                    out.print(childValue);
-                } else if (lineage.size() - 1 == lineageIndex) {
-                    out.print("(this Map)");
-                } else {
-                    out.print(
-                        "(ancestor["
-                            + (lineage.size() - 1 - lineageIndex - 1)
-                            + "] Map)");
-                }
-
-                if (debug && childValue != null) {
-                    out.print(' ');
-                    out.println(childValue.getClass().getName());
-                } else {
-                    out.println();
-                }
-            }
+    /**
+     * Gets a Short from a Map in a null-safe manner.
+     * <p>
+     * The Short is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
+     *
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a Short, <code>null</code> if null map input
+     */
+    public static <K> Short getShort(final Map<? super K, ?> map, final K key) {
+        final Number answer = getNumber(map, key);
+        if (answer == null) {
+            return null;
         }
+        if (answer instanceof Short) {
+            return (Short) answer;
+        }
+        return Short.valueOf(answer.shortValue());
+    }
 
-        lineage.removeLast();
-
-        printIndent(out, lineage.size());
-        out.println(debug ? "} " + map.getClass().getName() : "}");
+    /**
+     * Looks up the given key in the given map, converting the result into a short, using the defaultFunction to produce
+     * the default value if the conversion fails.
+     *
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultFunction what to produce the default value if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue produced by the defaultFunction if the original value
+     *         is null, the map is null or the number conversion fails
+     * @since 4.5
+     */
+    public static <K> Short getShort(final Map<? super K, ?> map, final K key,
+            final Function<K, Short> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getShort, defaultFunction);
     }
 
     /**
-     * Writes indentation to the given stream.
+     * Looks up the given key in the given map, converting the result into a short, using the default value if the
+     * conversion fails.
      *
-     * @param out  the stream to indent
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultValue what to return if the value is null or if the conversion fails
+     * @return the value in the map as a number, or defaultValue if the original value is null, the map is null or the
+     *         number conversion fails
      */
-    private static void printIndent(final PrintStream out, final int indent) {
-        for (int i = 0; i < indent; i++) {
-            out.print(INDENT_STRING);
-        }
+    public static <K> Short getShort(final Map<? super K, ?> map, final K key, final Short defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getShort, defaultValue);
     }
 
-    // Misc
-    //-----------------------------------------------------------------------
     /**
-     * Inverts the supplied map returning a new HashMap such that the keys of
-     * the input are swapped with the values.
+     * Gets a short from a Map in a null-safe manner.
      * <p>
-     * This operation assumes that the inverse mapping is well defined.
-     * If the input map had multiple entries with the same value mapped to
-     * different keys, the returned map will map one of those keys to the
-     * value, but the exact key which will be mapped is undefined.
+     * The short is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to invert, may not be null
-     * @return a new HashMap containing the inverted data
-     * @throws NullPointerException if the map is null
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a short, <code>0</code> if null map input
      */
-    public static <K, V> Map<V, K> invertMap(final Map<K, V> map) {
-        final Map<V, K> out = new HashMap<>(map.size());
-        for (final Entry<K, V> entry : map.entrySet()) {
-            out.put(entry.getValue(), entry.getKey());
-        }
-        return out;
+    public static <K> short getShortValue(final Map<? super K, ?> map, final K key) {
+        return applyDefaultValue(map, key, MapUtils::getShort, 0).shortValue();
     }
 
-    //-----------------------------------------------------------------------
     /**
-     * Protects against adding null values to a map.
-     * <p>
-     * This method checks the value being added to the map, and if it is null
-     * it is replaced by an empty string.
-     * <p>
-     * This could be useful if the map does not accept null values, or for
-     * receiving data from a source that may provide null or empty string
-     * which should be held in the same way in the map.
+     * Gets a short from a Map in a null-safe manner, using the default value produced by the defaultFunction if the
+     * conversion fails.
      * <p>
-     * Keys are not validated.
-     * Note that this method can be used to circumvent the map's
-     * value type at runtime.
+     * The short is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param map  the map to add to, may not be null
-     * @param key  the key
-     * @param value  the value, null converted to ""
-     * @throws NullPointerException if the map is null
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultFunction produce the default value to return if the value is null or if the conversion fails
+     * @return the value in the Map as a short, default value produced by the <code>defaultFunction</code> if null map
+     *         input
+     * @since 4.5
      */
-    public static <K> void safeAddToMap(final Map<? super K, Object> map, final K key, final Object value)
-            throws NullPointerException {
-        map.put(key, value == null ? "" : value);
+    public static <K> short getShortValue(final Map<? super K, ?> map, final K key,
+            final Function<K, Short> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getShort, defaultFunction, (short) 0).shortValue();
     }
 
-    //-----------------------------------------------------------------------
     /**
-     * Puts all the keys and values from the specified array into the map.
-     * <p>
-     * This method is an alternative to the {@link java.util.Map#putAll(java.util.Map)}
-     * method and constructors. It allows you to build a map from an object array
-     * of various possible styles.
+     * Gets a short from a Map in a null-safe manner, using the default value if the conversion fails.
      * <p>
-     * If the first entry in the object array implements {@link java.util.Map.Entry}
-     * or {@link KeyValue} then the key and value are added from that object.
-     * If the first entry in the object array is an object array itself, then
-     * it is assumed that index 0 in the sub-array is the key and index 1 is the value.
-     * Otherwise, the array is treated as keys and values in alternate indices.
-     * <p>
-     * For example, to create a color map:
-     * <pre>
-     * Map colorMap = MapUtils.putAll(new HashMap(), new String[][] {
-     *     {"RED", "#FF0000"},
-     *     {"GREEN", "#00FF00"},
-     *     {"BLUE", "#0000FF"}
-     * });
-     * </pre>
-     * or:
-     * <pre>
-     * Map colorMap = MapUtils.putAll(new HashMap(), new String[] {
-     *     "RED", "#FF0000",
-     *     "GREEN", "#00FF00",
-     *     "BLUE", "#0000FF"
-     * });
-     * </pre>
-     * or:
-     * <pre>
-     * Map colorMap = MapUtils.putAll(new HashMap(), new Map.Entry[] {
-     *     new DefaultMapEntry("RED", "#FF0000"),
-     *     new DefaultMapEntry("GREEN", "#00FF00"),
-     *     new DefaultMapEntry("BLUE", "#0000FF")
-     * });
-     * </pre>
+     * The short is obtained from the results of {@link #getNumber(Map,Object)}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to populate, must not be null
-     * @param array  an array to populate from, null ignored
-     * @return the input map
-     * @throws NullPointerException  if map is null
-     * @throws IllegalArgumentException  if sub-array or entry matching used and an entry is invalid
-     * @throws ClassCastException if the array contents is mixed
-     * @since 3.2
+     * @param <K> the key type
+     * @param map the map to use
+     * @param key the key to look up
+     * @param defaultValue return if the value is null or if the conversion fails
+     * @return the value in the Map as a short, <code>defaultValue</code> if null map input
      */
-    @SuppressWarnings("unchecked") // As per Javadoc throws CCE for invalid array contents
-    public static <K, V> Map<K, V> putAll(final Map<K, V> map, final Object[] array) {
-        if (map == null) {
-            throw new NullPointerException("The map must not be null");
-        }
-        if (array == null || array.length == 0) {
-            return map;
-        }
-        final Object obj = array[0];
-        if (obj instanceof Map.Entry) {
-            for (final Object element : array) {
-                // cast ok here, type is checked above
-                final Map.Entry<K, V> entry = (Map.Entry<K, V>) element;
-                map.put(entry.getKey(), entry.getValue());
-            }
-        } else if (obj instanceof KeyValue) {
-            for (final Object element : array) {
-                // cast ok here, type is checked above
-                final KeyValue<K, V> keyval = (KeyValue<K, V>) element;
-                map.put(keyval.getKey(), keyval.getValue());
-            }
-        } else if (obj instanceof Object[]) {
-            for (int i = 0; i < array.length; i++) {
-                final Object[] sub = (Object[]) array[i];
-                if (sub == null || sub.length < 2) {
-                    throw new IllegalArgumentException("Invalid array element: " + i);
-                }
-                // these casts can fail if array has incorrect types
-                map.put((K) sub[0], (V) sub[1]);
-            }
-        } else {
-            for (int i = 0; i < array.length - 1;) {
-                // these casts can fail if array has incorrect types
-                map.put((K) array[i++], (V) array[i++]);
-            }
-        }
-        return map;
+    public static <K> short getShortValue(final Map<? super K, ?> map, final K key, final short defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getShort, defaultValue).shortValue();
     }
 
-    //-----------------------------------------------------------------------
-
     /**
-     * Returns an immutable empty map if the argument is <code>null</code>,
-     * or the argument itself otherwise.
+     * Gets a String from a Map in a null-safe manner.
+     * <p>
+     * The String is obtained via <code>toString</code>.
+     * </p>
      *
      * @param <K> the key type
-     * @param <V> the value type
-     * @param map the map, possibly <code>null</code>
-     * @return an empty map if the argument is <code>null</code>
+     * @param map the map to use
+     * @param key the key to look up
+     * @return the value in the Map as a String, <code>null</code> if null map input
      */
-    public static <K,V> Map<K,V> emptyIfNull(final Map<K,V> map) {
-        return map == null ? Collections.<K,V>emptyMap() : map;
+    public static <K> String getString(final Map<? super K, ?> map, final K key) {
+        if (map != null) {
+            final Object answer = map.get(key);
+            if (answer != null) {
+                return answer.toString();
+            }
+        }
+        return null;
     }
 
     /**
-     * Null-safe check if the specified map is empty.
-     * <p>
-     * Null returns true.
+     * Looks up the given key in the given map, converting the result into a string, using the defaultFunction to
+     * produce the default value if the conversion fails.
      *
-     * @param map  the map to check, may be null
-     * @return true if empty or null
-     * @since 3.2
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultFunction what to produce the default value if the value is null or if the conversion fails
+     * @return the value in the map as a string, or defaultValue produced by the defaultFunction if the original value
+     *         is null, the map is null or the string conversion fails
+     * @since 4.5
      */
-    public static boolean isEmpty(final Map<?,?> map) {
-        return map == null || map.isEmpty();
+    public static <K> String getString(final Map<? super K, ?> map, final K key,
+            final Function<K, String> defaultFunction) {
+        return applyDefaultFunction(map, key, MapUtils::getString, defaultFunction);
     }
 
     /**
-     * Null-safe check if the specified map is not empty.
-     * <p>
-     * Null returns false.
+     * Looks up the given key in the given map, converting the result into a string, using the default value if the
+     * conversion fails.
      *
-     * @param map  the map to check, may be null
-     * @return true if non-null and non-empty
-     * @since 3.2
+     * @param <K> the key type
+     * @param map the map whose value to look up
+     * @param key the key of the value to look up in that map
+     * @param defaultValue what to return if the value is null or if the conversion fails
+     * @return the value in the map as a string, or defaultValue if the original value is null, the map is null or the
+     *         string conversion fails
      */
-    public static boolean isNotEmpty(final Map<?,?> map) {
-        return !MapUtils.isEmpty(map);
+    public static <K> String getString(final Map<? super K, ?> map, final K key, final String defaultValue) {
+        return applyDefaultValue(map, key, MapUtils::getString, defaultValue);
     }
 
-    // Map decorators
-    //-----------------------------------------------------------------------
+    // Misc
+    // -----------------------------------------------------------------------
     /**
-     * Returns a synchronized map backed by the given map.
+     * Inverts the supplied map returning a new HashMap such that the keys of the input are swapped with the values.
      * <p>
-     * You must manually synchronize on the returned buffer's iterator to
-     * avoid non-deterministic behavior:
-     *
-     * <pre>
-     * Map m = MapUtils.synchronizedMap(myMap);
-     * Set s = m.keySet();  // outside synchronized block
-     * synchronized (m) {  // synchronized on MAP!
-     *     Iterator i = s.iterator();
-     *     while (i.hasNext()) {
-     *         process (i.next());
-     *     }
-     * }
-     * </pre>
-     *
-     * This method uses the implementation in {@link java.util.Collections Collections}.
+     * This operation assumes that the inverse mapping is well defined. If the input map had multiple entries with the
+     * same value mapped to different keys, the returned map will map one of those keys to the value, but the exact key
+     * which will be mapped is undefined.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to synchronize, must not be null
-     * @return a synchronized map backed by the given map
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to invert, may not be null
+     * @return a new HashMap containing the inverted data
+     * @throws NullPointerException if the map is null
      */
-    public static <K, V> Map<K, V> synchronizedMap(final Map<K, V> map) {
-        return Collections.synchronizedMap(map);
+    public static <K, V> Map<V, K> invertMap(final Map<K, V> map) {
+        final Map<V, K> out = new HashMap<>(map.size());
+        for (final Entry<K, V> entry : map.entrySet()) {
+            out.put(entry.getValue(), entry.getKey());
+        }
+        return out;
     }
 
     /**
-     * Returns an unmodifiable map backed by the given map.
+     * Null-safe check if the specified map is empty.
      * <p>
-     * This method uses the implementation in the decorators subpackage.
+     * Null returns true.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to make unmodifiable, must not be null
-     * @return an unmodifiable map backed by the given map
-     * @throws NullPointerException  if the map is null
+     * @param map the map to check, may be null
+     * @return true if empty or null
+     * @since 3.2
      */
-    public static <K, V> Map<K, V> unmodifiableMap(final Map<? extends K, ? extends V> map) {
-        return UnmodifiableMap.unmodifiableMap(map);
+    public static boolean isEmpty(final Map<?, ?> map) {
+        return map == null || map.isEmpty();
     }
 
     /**
-     * Returns a predicated (validating) map backed by the given map.
+     * Null-safe check if the specified map is not empty.
      * <p>
-     * Only objects that pass the tests in the given predicates can be added to the map.
-     * Trying to add an invalid object results in an IllegalArgumentException.
-     * Keys must pass the key predicate, values must pass the value predicate.
-     * It is important not to use the original map after invoking this method,
-     * as it is a backdoor for adding invalid objects.
-     *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to predicate, must not be null
-     * @param keyPred  the predicate for keys, null means no check
-     * @param valuePred  the predicate for values, null means no check
-     * @return a predicated map backed by the given map
-     * @throws NullPointerException  if the Map is null
+     * Null returns false.
+     * </p>
+     *
+     * @param map the map to check, may be null
+     * @return true if non-null and non-empty
+     * @since 3.2
      */
-    public static <K, V> IterableMap<K, V> predicatedMap(final Map<K, V> map, final Predicate<? super K> keyPred,
-                                                         final Predicate<? super V> valuePred) {
-        return PredicatedMap.predicatedMap(map, keyPred, valuePred);
+    public static boolean isNotEmpty(final Map<?, ?> map) {
+        return !MapUtils.isEmpty(map);
     }
 
     /**
-     * Returns a transformed map backed by the given map.
-     * <p>
-     * This method returns a new map (decorating the specified map) that
-     * will transform any new entries added to it.
-     * Existing entries in the specified map will not be transformed.
-     * If you want that behaviour, see {@link TransformedMap#transformedMap}.
-     * <p>
-     * Each object is passed through the transformers as it is added to the
-     * Map. It is important not to use the original map after invoking this
-     * method, as it is a backdoor for adding untransformed objects.
-     * <p>
-     * If there are any elements already in the map being decorated, they
-     * are NOT transformed.
+     * Get the specified {@link Map} as an {@link IterableMap}.
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to transform, must not be null, typically empty
-     * @param keyTransformer  the transformer for the map keys, null means no transformation
-     * @param valueTransformer  the transformer for the map values, null means no transformation
-     * @return a transformed map backed by the given map
-     * @throws NullPointerException  if the Map is null
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map to wrap if necessary.
+     * @return IterableMap&lt;K, V&gt;
+     * @throws NullPointerException if map is null
+     * @since 4.0
      */
-    public static <K, V> IterableMap<K, V> transformedMap(final Map<K, V> map,
-            final Transformer<? super K, ? extends K> keyTransformer,
-            final Transformer<? super V, ? extends V> valueTransformer) {
-        return TransformedMap.transformingMap(map, keyTransformer, valueTransformer);
+    public static <K, V> IterableMap<K, V> iterableMap(final Map<K, V> map) {
+        if (map == null) {
+            throw new NullPointerException("Map must not be null");
+        }
+        return map instanceof IterableMap ? (IterableMap<K, V>) map : new AbstractMapDecorator<K, V>(map) {
+        };
     }
 
     /**
-     * Returns a fixed-sized map backed by the given map.
-     * Elements may not be added or removed from the returned map, but
-     * existing elements can be changed (for instance, via the
-     * {@link Map#put(Object,Object)} method).
+     * Get the specified {@link SortedMap} as an {@link IterableSortedMap}.
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map whose size to fix, must not be null
-     * @return a fixed-size map backed by that map
-     * @throws NullPointerException  if the Map is null
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param sortedMap to wrap if necessary
+     * @return {@link IterableSortedMap}&lt;K, V&gt;
+     * @throws NullPointerException if sortedMap is null
+     * @since 4.0
      */
-    public static <K, V> IterableMap<K, V> fixedSizeMap(final Map<K, V> map) {
-        return FixedSizeMap.fixedSizeMap(map);
+    public static <K, V> IterableSortedMap<K, V> iterableSortedMap(final SortedMap<K, V> sortedMap) {
+        if (sortedMap == null) {
+            throw new NullPointerException("Map must not be null");
+        }
+        return sortedMap instanceof IterableSortedMap ? (IterableSortedMap<K, V>) sortedMap
+                : new AbstractSortedMapDecorator<K, V>(sortedMap) {
+                };
     }
 
     /**
      * Returns a "lazy" map whose values will be created on demand.
      * <p>
-     * When the key passed to the returned map's {@link Map#get(Object)}
-     * method is not present in the map, then the factory will be used
-     * to create a new object and that object will become the value
-     * associated with that key.
+     * When the key passed to the returned map's {@link Map#get(Object)} method is not present in the map, then the
+     * factory will be used to create a new object and that object will become the value associated with that key.
+     * </p>
      * <p>
      * For instance:
+     * </p>
      * <pre>
      * Factory factory = new Factory() {
      *     public Object create() {
@@ -1359,31 +1282,34 @@ public class MapUtils {
      * Object obj = lazyMap.get("test");
      * </pre>
      *
-     * After the above code is executed, <code>obj</code> will contain
-     * a new <code>Date</code> instance.  Furthermore, that <code>Date</code>
-     * instance is the value for the <code>"test"</code> key in the map.
+     * <p>
+     * After the above code is executed, <code>obj</code> will contain a new <code>Date</code> instance. Furthermore,
+     * that <code>Date</code> instance is the value for the <code>"test"</code> key in the map.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to make lazy, must not be null
-     * @param factory  the factory for creating new objects, must not be null
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to make lazy, must not be null
+     * @param factory the factory for creating new objects, must not be null
      * @return a lazy map backed by the given map
-     * @throws NullPointerException  if the Map or Factory is null
+     * @throws NullPointerException if the Map or Factory is null
      */
     public static <K, V> IterableMap<K, V> lazyMap(final Map<K, V> map, final Factory<? extends V> factory) {
         return LazyMap.lazyMap(map, factory);
     }
 
+    // -----------------------------------------------------------------------
+
     /**
      * Returns a "lazy" map whose values will be created on demand.
      * <p>
-     * When the key passed to the returned map's {@link Map#get(Object)}
-     * method is not present in the map, then the factory will be used
-     * to create a new object and that object will become the value
-     * associated with that key. The factory is a {@link Transformer}
-     * that will be passed the key which it must transform into the value.
+     * When the key passed to the returned map's {@link Map#get(Object)} method is not present in the map, then the
+     * factory will be used to create a new object and that object will become the value associated with that key. The
+     * factory is a {@link Transformer} that will be passed the key which it must transform into the value.
+     * </p>
      * <p>
      * For instance:
+     * </p>
      * <pre>
      * Transformer factory = new Transformer() {
      *     public Object transform(Object mapKey) {
@@ -1394,22 +1320,23 @@ public class MapUtils {
      * Object obj = lazyMap.get("C:/dev");
      * </pre>
      *
-     * After the above code is executed, <code>obj</code> will contain
-     * a new <code>File</code> instance for the C drive dev directory.
-     * Furthermore, that <code>File</code> instance is the value for the
-     * <code>"C:/dev"</code> key in the map.
      * <p>
-     * If a lazy map is wrapped by a synchronized map, the result is a simple
-     * synchronized cache. When an object is not is the cache, the cache itself
-     * calls back to the factory Transformer to populate itself, all within the
-     * same synchronized block.
+     * After the above code is executed, <code>obj</code> will contain a new <code>File</code> instance for the C drive
+     * dev directory. Furthermore, that <code>File</code> instance is the value for the <code>"C:/dev"</code> key in the
+     * map.
+     * </p>
+     * <p>
+     * If a lazy map is wrapped by a synchronized map, the result is a simple synchronized cache. When an object is not
+     * is the cache, the cache itself calls back to the factory Transformer to populate itself, all within the same
+     * synchronized block.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to make lazy, must not be null
-     * @param transformerFactory  the factory for creating new objects, must not be null
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to make lazy, must not be null
+     * @param transformerFactory the factory for creating new objects, must not be null
      * @return a lazy map backed by the given map
-     * @throws NullPointerException  if the Map or Transformer is null
+     * @throws NullPointerException if the Map or Transformer is null
      */
     public static <K, V> IterableMap<K, V> lazyMap(final Map<K, V> map,
             final Transformer<? super K, ? extends V> transformerFactory) {
@@ -1417,29 +1344,87 @@ public class MapUtils {
     }
 
     /**
-     * Returns a map that maintains the order of keys that are added
-     * backed by the given map.
+     * Returns a "lazy" sorted map whose values will be created on demand.
      * <p>
-     * If a key is added twice, the order is determined by the first add.
-     * The order is observed through the keySet, values and entrySet.
+     * When the key passed to the returned map's {@link Map#get(Object)} method is not present in the map, then the
+     * factory will be used to create a new object and that object will become the value associated with that key.
+     * </p>
+     * <p>
+     * For instance:
+     * </p>
+     * <pre>
+     * Factory factory = new Factory() {
+     *     public Object create() {
+     *         return new Date();
+     *     }
+     * }
+     * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory);
+     * Object obj = lazy.get("test");
+     * </pre>
+     * <p>
+     * After the above code is executed, <code>obj</code> will contain a new <code>Date</code> instance. Furthermore,
+     * that <code>Date</code> instance is the value for the <code>"test"</code> key.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to order, must not be null
-     * @return an ordered map backed by the given map
-     * @throws NullPointerException  if the Map is null
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to make lazy, must not be null
+     * @param factory the factory for creating new objects, must not be null
+     * @return a lazy map backed by the given map
+     * @throws NullPointerException if the SortedMap or Factory is null
      */
-    public static <K, V> OrderedMap<K, V> orderedMap(final Map<K, V> map) {
-        return ListOrderedMap.listOrderedMap(map);
+    public static <K, V> SortedMap<K, V> lazySortedMap(final SortedMap<K, V> map, final Factory<? extends V> factory) {
+        return LazySortedMap.lazySortedMap(map, factory);
+    }
+
+    /**
+     * Returns a "lazy" sorted map whose values will be created on demand.
+     * <p>
+     * When the key passed to the returned map's {@link Map#get(Object)} method is not present in the map, then the
+     * factory will be used to create a new object and that object will become the value associated with that key. The
+     * factory is a {@link Transformer} that will be passed the key which it must transform into the value.
+     * </p>
+     * <p>
+     * For instance:
+     * </p>
+     * <pre>
+     * Transformer factory = new Transformer() {
+     *     public Object transform(Object mapKey) {
+     *         return new File(mapKey);
+     *     }
+     * }
+     * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory);
+     * Object obj = lazy.get("C:/dev");
+     * </pre>
+     * <p>
+     * After the above code is executed, <code>obj</code> will contain a new <code>File</code> instance for the C drive
+     * dev directory. Furthermore, that <code>File</code> instance is the value for the <code>"C:/dev"</code> key in the
+     * map.
+     * </p>
+     * <p>
+     * If a lazy map is wrapped by a synchronized map, the result is a simple synchronized cache. When an object is not
+     * is the cache, the cache itself calls back to the factory Transformer to populate itself, all within the same
+     * synchronized block.
+     * </p>
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to make lazy, must not be null
+     * @param transformerFactory the factory for creating new objects, must not be null
+     * @return a lazy map backed by the given map
+     * @throws NullPointerException if the Map or Transformer is null
+     */
+    public static <K, V> SortedMap<K, V> lazySortedMap(final SortedMap<K, V> map,
+            final Transformer<? super K, ? extends V> transformerFactory) {
+        return LazySortedMap.lazySortedMap(map, transformerFactory);
     }
 
     /**
-     * Creates a mult-value map backed by the given map which returns
-     * collections of type ArrayList.
+     * Creates a mult-value map backed by the given map which returns collections of type ArrayList.
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to decorate
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to decorate
      * @return a multi-value map backed by the given map which returns ArrayLists of values.
      * @see MultiValueMap
      * @since 3.2
@@ -1451,15 +1436,14 @@ public class MapUtils {
     }
 
     /**
-     * Creates a multi-value map backed by the given map which returns
-     * collections of the specified type.
+     * Creates a multi-value map backed by the given map which returns collections of the specified type.
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param <C>  the collection class type
-     * @param map  the map to decorate
-     * @param collectionClass  the type of collections to return from the map
-     *   (must contain public no-arg constructor and extend Collection)
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param <C> the collection class type
+     * @param map the map to decorate
+     * @param collectionClass the type of collections to return from the map (must contain public no-arg constructor and
+     *        extend Collection)
      * @return a multi-value map backed by the given map which returns collections of the specified type
      * @see MultiValueMap
      * @since 3.2
@@ -1472,16 +1456,16 @@ public class MapUtils {
     }
 
     /**
-     * Creates a multi-value map backed by the given map which returns
-     * collections created by the specified collection factory.
+     * Creates a multi-value map backed by the given map which returns collections created by the specified collection
+     * factory.
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param <C>  the collection class type
-     * @param map  the map to decorate
-     * @param collectionFactory  a factor which creates collection objects
-     * @return a multi-value map backed by the given map which returns collections
-     *   created by the specified collection factory
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param <C> the collection class type
+     * @param map the map to decorate
+     * @param collectionFactory a factor which creates collection objects
+     * @return a multi-value map backed by the given map which returns collections created by the specified collection
+     *         factory
      * @see MultiValueMap
      * @since 3.2
      * @deprecated since 4.1, use {@link MultiValuedMap} instead
@@ -1492,68 +1476,135 @@ public class MapUtils {
         return MultiValueMap.multiValueMap(map, collectionFactory);
     }
 
-    // SortedMap decorators
-    //-----------------------------------------------------------------------
     /**
-     * Returns a synchronized sorted map backed by the given sorted map.
+     * Returns a map that maintains the order of keys that are added backed by the given map.
      * <p>
-     * You must manually synchronize on the returned buffer's iterator to
-     * avoid non-deterministic behavior:
+     * If a key is added twice, the order is determined by the first add. The order is observed through the keySet,
+     * values and entrySet.
+     * </p>
      *
-     * <pre>
-     * Map m = MapUtils.synchronizedSortedMap(myMap);
-     * Set s = m.keySet();  // outside synchronized block
-     * synchronized (m) {  // synchronized on MAP!
-     *     Iterator i = s.iterator();
-     *     while (i.hasNext()) {
-     *         process (i.next());
-     *     }
-     * }
-     * </pre>
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to order, must not be null
+     * @return an ordered map backed by the given map
+     * @throws NullPointerException if the Map is null
+     */
+    public static <K, V> OrderedMap<K, V> orderedMap(final Map<K, V> map) {
+        return ListOrderedMap.listOrderedMap(map);
+    }
+
+    /**
+     * Populates a Map using the supplied <code>Transformer</code>s to transform the elements into keys and values.
      *
-     * This method uses the implementation in {@link java.util.Collections Collections}.
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param <E> the type of object contained in the {@link Iterable}
+     * @param map the <code>Map</code> to populate.
+     * @param elements the <code>Iterable</code> containing the input values for the map.
+     * @param keyTransformer the <code>Transformer</code> used to transform the element into a key value
+     * @param valueTransformer the <code>Transformer</code> used to transform the element into a value
+     * @throws NullPointerException if the map, elements or transformers are null
+     */
+    public static <K, V, E> void populateMap(final Map<K, V> map, final Iterable<? extends E> elements,
+            final Transformer<E, K> keyTransformer, final Transformer<E, V> valueTransformer) {
+        final Iterator<? extends E> iter = elements.iterator();
+        while (iter.hasNext()) {
+            final E temp = iter.next();
+            map.put(keyTransformer.transform(temp), valueTransformer.transform(temp));
+        }
+    }
+
+    /**
+     * Populates a Map using the supplied <code>Transformer</code> to transform the elements into keys, using the
+     * unaltered element as the value in the <code>Map</code>.
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to synchronize, must not be null
-     * @return a synchronized map backed by the given map
-     * @throws NullPointerException  if the map is null
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the <code>Map</code> to populate.
+     * @param elements the <code>Iterable</code> containing the input values for the map.
+     * @param keyTransformer the <code>Transformer</code> used to transform the element into a key value
+     * @throws NullPointerException if the map, elements or transformer are null
      */
-    public static <K, V> SortedMap<K, V> synchronizedSortedMap(final SortedMap<K, V> map) {
-        return Collections.synchronizedSortedMap(map);
+    public static <K, V> void populateMap(final Map<K, V> map, final Iterable<? extends V> elements,
+            final Transformer<V, K> keyTransformer) {
+        populateMap(map, elements, keyTransformer, TransformerUtils.<V>nopTransformer());
     }
 
     /**
-     * Returns an unmodifiable sorted map backed by the given sorted map.
+     * Populates a MultiMap using the supplied <code>Transformer</code>s to transform the elements into keys and values.
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param <E> the type of object contained in the {@link Iterable}
+     * @param map the <code>MultiMap</code> to populate.
+     * @param elements the <code>Iterable</code> containing the input values for the map.
+     * @param keyTransformer the <code>Transformer</code> used to transform the element into a key value
+     * @param valueTransformer the <code>Transformer</code> used to transform the element into a value
+     * @throws NullPointerException if the map, collection or transformers are null
+     */
+    public static <K, V, E> void populateMap(final MultiMap<K, V> map, final Iterable<? extends E> elements,
+            final Transformer<E, K> keyTransformer, final Transformer<E, V> valueTransformer) {
+        final Iterator<? extends E> iter = elements.iterator();
+        while (iter.hasNext()) {
+            final E temp = iter.next();
+            map.put(keyTransformer.transform(temp), valueTransformer.transform(temp));
+        }
+    }
+
+    /**
+     * Populates a MultiMap using the supplied <code>Transformer</code> to transform the elements into keys, using the
+     * unaltered element as the value in the <code>MultiMap</code>.
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the <code>MultiMap</code> to populate.
+     * @param elements the <code>Iterable</code> to use as input values for the map.
+     * @param keyTransformer the <code>Transformer</code> used to transform the element into a key value
+     * @throws NullPointerException if the map, elements or transformer are null
+     */
+    public static <K, V> void populateMap(final MultiMap<K, V> map, final Iterable<? extends V> elements,
+            final Transformer<V, K> keyTransformer) {
+        populateMap(map, elements, keyTransformer, TransformerUtils.<V>nopTransformer());
+    }
+
+    /**
+     * Returns a predicated (validating) map backed by the given map.
      * <p>
-     * This method uses the implementation in the decorators subpackage.
+     * Only objects that pass the tests in the given predicates can be added to the map. Trying to add an invalid object
+     * results in an IllegalArgumentException. Keys must pass the key predicate, values must pass the value predicate.
+     * It is important not to use the original map after invoking this method, as it is a backdoor for adding invalid
+     * objects.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the sorted map to make unmodifiable, must not be null
-     * @return an unmodifiable map backed by the given map
-     * @throws NullPointerException  if the map is null
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to predicate, must not be null
+     * @param keyPred the predicate for keys, null means no check
+     * @param valuePred the predicate for values, null means no check
+     * @return a predicated map backed by the given map
+     * @throws NullPointerException if the Map is null
      */
-    public static <K, V> SortedMap<K, V> unmodifiableSortedMap(final SortedMap<K, ? extends V> map) {
-        return UnmodifiableSortedMap.unmodifiableSortedMap(map);
+    public static <K, V> IterableMap<K, V> predicatedMap(final Map<K, V> map, final Predicate<? super K> keyPred,
+            final Predicate<? super V> valuePred) {
+        return PredicatedMap.predicatedMap(map, keyPred, valuePred);
     }
 
     /**
      * Returns a predicated (validating) sorted map backed by the given map.
      * <p>
-     * Only objects that pass the tests in the given predicates can be added to the map.
-     * Trying to add an invalid object results in an IllegalArgumentException.
-     * Keys must pass the key predicate, values must pass the value predicate.
-     * It is important not to use the original map after invoking this method,
-     * as it is a backdoor for adding invalid objects.
-     *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to predicate, must not be null
-     * @param keyPred  the predicate for keys, null means no check
-     * @param valuePred  the predicate for values, null means no check
+     * Only objects that pass the tests in the given predicates can be added to the map. Trying to add an invalid object
+     * results in an IllegalArgumentException. Keys must pass the key predicate, values must pass the value predicate.
+     * It is important not to use the original map after invoking this method, as it is a backdoor for adding invalid
+     * objects.
+     * </p>
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to predicate, must not be null
+     * @param keyPred the predicate for keys, null means no check
+     * @param valuePred the predicate for values, null means no check
      * @return a predicated map backed by the given map
-     * @throws NullPointerException  if the SortedMap is null
+     * @throws NullPointerException if the SortedMap is null
      */
     public static <K, V> SortedMap<K, V> predicatedSortedMap(final SortedMap<K, V> map,
             final Predicate<? super K> keyPred, final Predicate<? super V> valuePred) {
@@ -1561,247 +1612,440 @@ public class MapUtils {
     }
 
     /**
-     * Returns a transformed sorted map backed by the given map.
+     * Writes indentation to the given stream.
+     *
+     * @param out the stream to indent
+     */
+    private static void printIndent(final PrintStream out, final int indent) {
+        for (int i = 0; i < indent; i++) {
+            out.print(INDENT_STRING);
+        }
+    }
+
+    // -----------------------------------------------------------------------
+    /**
+     * Puts all the keys and values from the specified array into the map.
      * <p>
-     * This method returns a new sorted map (decorating the specified map) that
-     * will transform any new entries added to it.
-     * Existing entries in the specified map will not be transformed.
-     * If you want that behaviour, see {@link TransformedSortedMap#transformedSortedMap}.
+     * This method is an alternative to the {@link java.util.Map#putAll(java.util.Map)} method and constructors. It
+     * allows you to build a map from an object array of various possible styles.
+     * </p>
      * <p>
-     * Each object is passed through the transformers as it is added to the
-     * Map. It is important not to use the original map after invoking this
-     * method, as it is a backdoor for adding untransformed objects.
+     * If the first entry in the object array implements {@link java.util.Map.Entry} or {@link KeyValue} then the key
+     * and value are added from that object. If the first entry in the object array is an object array itself, then it
+     * is assumed that index 0 in the sub-array is the key and index 1 is the value. Otherwise, the array is treated as
+     * keys and values in alternate indices.
+     * </p>
      * <p>
-     * If there are any elements already in the map being decorated, they
-     * are NOT transformed.
+     * For example, to create a color map:
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to transform, must not be null, typically empty
-     * @param keyTransformer  the transformer for the map keys, null means no transformation
-     * @param valueTransformer  the transformer for the map values, null means no transformation
-     * @return a transformed map backed by the given map
-     * @throws NullPointerException  if the SortedMap is null
+     * <pre>
+     * Map colorMap = MapUtils.putAll(new HashMap(),
+     *         new String[][] { { "RED", "#FF0000" }, { "GREEN", "#00FF00" }, { "BLUE", "#0000FF" } });
+     * </pre>
+     *
+     * <p>
+     * or:
+     * </p>
+     *
+     * <pre>
+     * Map colorMap = MapUtils.putAll(new HashMap(),
+     *         new String[] { "RED", "#FF0000", "GREEN", "#00FF00", "BLUE", "#0000FF" });
+     * </pre>
+     *
+     * <p>
+     * or:
+     * </p>
+     *
+     * <pre>
+     * Map colorMap = MapUtils.putAll(new HashMap(), new Map.Entry[] { new DefaultMapEntry("RED", "#FF0000"),
+     *         new DefaultMapEntry("GREEN", "#00FF00"), new DefaultMapEntry("BLUE", "#0000FF") });
+     * </pre>
+     *
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to populate, must not be null
+     * @param array an array to populate from, null ignored
+     * @return the input map
+     * @throws NullPointerException if map is null
+     * @throws IllegalArgumentException if sub-array or entry matching used and an entry is invalid
+     * @throws ClassCastException if the array contents is mixed
+     * @since 3.2
      */
-    public static <K, V> SortedMap<K, V> transformedSortedMap(final SortedMap<K, V> map,
-            final Transformer<? super K, ? extends K> keyTransformer,
-            final Transformer<? super V, ? extends V> valueTransformer) {
-        return TransformedSortedMap.transformingSortedMap(map, keyTransformer, valueTransformer);
+    @SuppressWarnings("unchecked") // As per Javadoc throws CCE for invalid array contents
+    public static <K, V> Map<K, V> putAll(final Map<K, V> map, final Object[] array) {
+        if (map == null) {
+            throw new NullPointerException("The map must not be null");
+        }
+        if (array == null || array.length == 0) {
+            return map;
+        }
+        final Object obj = array[0];
+        if (obj instanceof Map.Entry) {
+            for (final Object element : array) {
+                // cast ok here, type is checked above
+                final Map.Entry<K, V> entry = (Map.Entry<K, V>) element;
+                map.put(entry.getKey(), entry.getValue());
+            }
+        } else if (obj instanceof KeyValue) {
+            for (final Object element : array) {
+                // cast ok here, type is checked above
+                final KeyValue<K, V> keyval = (KeyValue<K, V>) element;
+                map.put(keyval.getKey(), keyval.getValue());
+            }
+        } else if (obj instanceof Object[]) {
+            for (int i = 0; i < array.length; i++) {
+                final Object[] sub = (Object[]) array[i];
+                if (sub == null || sub.length < 2) {
+                    throw new IllegalArgumentException("Invalid array element: " + i);
+                }
+                // these casts can fail if array has incorrect types
+                map.put((K) sub[0], (V) sub[1]);
+            }
+        } else {
+            for (int i = 0; i < array.length - 1;) {
+                // these casts can fail if array has incorrect types
+                map.put((K) array[i++], (V) array[i++]);
+            }
+        }
+        return map;
     }
 
     /**
-     * Returns a fixed-sized sorted map backed by the given sorted map.
-     * Elements may not be added or removed from the returned map, but
-     * existing elements can be changed (for instance, via the
-     * {@link Map#put(Object,Object)} method).
+     * Protects against adding null values to a map.
+     * <p>
+     * This method checks the value being added to the map, and if it is null it is replaced by an empty string.
+     * </p>
+     * <p>
+     * This could be useful if the map does not accept null values, or for receiving data from a source that may provide
+     * null or empty string which should be held in the same way in the map.
+     * </p>
+     * <p>
+     * Keys are not validated. Note that this method can be used to circumvent the map's value type at runtime.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map whose size to fix, must not be null
-     * @return a fixed-size map backed by that map
-     * @throws NullPointerException  if the SortedMap is null
+     * @param <K> the key type
+     * @param map the map to add to, may not be null
+     * @param key the key
+     * @param value the value, null converted to ""
+     * @throws NullPointerException if the map is null
      */
-    public static <K, V> SortedMap<K, V> fixedSizeSortedMap(final SortedMap<K, V> map) {
-        return FixedSizeSortedMap.fixedSizeSortedMap(map);
+    public static <K> void safeAddToMap(final Map<? super K, Object> map, final K key, final Object value)
+            throws NullPointerException {
+        map.put(key, value == null ? "" : value);
     }
 
     /**
-     * Returns a "lazy" sorted map whose values will be created on demand.
-     * <p>
-     * When the key passed to the returned map's {@link Map#get(Object)}
-     * method is not present in the map, then the factory will be used
-     * to create a new object and that object will become the value
-     * associated with that key.
+     * Gets the given map size or 0 if the map is null
+     *
+     * @param map a Map or null
+     * @return the given map size or 0 if the map is null
+     */
+    public static int size(final Map<?, ?> map) {
+        return map == null ? 0 : map.size();
+    }
+
+    // -----------------------------------------------------------------------
+    /**
+     * Returns a synchronized map backed by the given map.
      * <p>
-     * For instance:
+     * You must manually synchronize on the returned buffer's iterator to avoid non-deterministic behavior:
+     * </p>
      *
      * <pre>
-     * Factory factory = new Factory() {
-     *     public Object create() {
-     *         return new Date();
+     * Map m = MapUtils.synchronizedMap(myMap);
+     * Set s = m.keySet(); // outside synchronized block
+     * synchronized (m) { // synchronized on MAP!
+     *     Iterator i = s.iterator();
+     *     while (i.hasNext()) {
+     *         process(i.next());
      *     }
      * }
-     * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory);
-     * Object obj = lazy.get("test");
      * </pre>
      *
-     * After the above code is executed, <code>obj</code> will contain
-     * a new <code>Date</code> instance.  Furthermore, that <code>Date</code>
-     * instance is the value for the <code>"test"</code> key.
+     * <p>
+     * This method uses the implementation in {@link java.util.Collections Collections}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to make lazy, must not be null
-     * @param factory  the factory for creating new objects, must not be null
-     * @return a lazy map backed by the given map
-     * @throws NullPointerException  if the SortedMap or Factory is null
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to synchronize, must not be null
+     * @return a synchronized map backed by the given map
      */
-    public static <K, V> SortedMap<K, V> lazySortedMap(final SortedMap<K, V> map, final Factory<? extends V> factory) {
-        return LazySortedMap.lazySortedMap(map, factory);
+    public static <K, V> Map<K, V> synchronizedMap(final Map<K, V> map) {
+        return Collections.synchronizedMap(map);
     }
 
+    // -----------------------------------------------------------------------
     /**
-     * Returns a "lazy" sorted map whose values will be created on demand.
-     * <p>
-     * When the key passed to the returned map's {@link Map#get(Object)}
-     * method is not present in the map, then the factory will be used
-     * to create a new object and that object will become the value
-     * associated with that key. The factory is a {@link Transformer}
-     * that will be passed the key which it must transform into the value.
+     * Returns a synchronized sorted map backed by the given sorted map.
      * <p>
-     * For instance:
+     * You must manually synchronize on the returned buffer's iterator to avoid non-deterministic behavior:
+     * </p>
+     *
      * <pre>
-     * Transformer factory = new Transformer() {
-     *     public Object transform(Object mapKey) {
-     *         return new File(mapKey);
+     * Map m = MapUtils.synchronizedSortedMap(myMap);
+     * Set s = m.keySet(); // outside synchronized block
+     * synchronized (m) { // synchronized on MAP!
+     *     Iterator i = s.iterator();
+     *     while (i.hasNext()) {
+     *         process(i.next());
      *     }
      * }
-     * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory);
-     * Object obj = lazy.get("C:/dev");
      * </pre>
      *
-     * After the above code is executed, <code>obj</code> will contain
-     * a new <code>File</code> instance for the C drive dev directory.
-     * Furthermore, that <code>File</code> instance is the value for the
-     * <code>"C:/dev"</code> key in the map.
      * <p>
-     * If a lazy map is wrapped by a synchronized map, the result is a simple
-     * synchronized cache. When an object is not is the cache, the cache itself
-     * calls back to the factory Transformer to populate itself, all within the
-     * same synchronized block.
+     * This method uses the implementation in {@link java.util.Collections Collections}.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map  the map to make lazy, must not be null
-     * @param transformerFactory  the factory for creating new objects, must not be null
-     * @return a lazy map backed by the given map
-     * @throws NullPointerException  if the Map or Transformer is null
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to synchronize, must not be null
+     * @return a synchronized map backed by the given map
+     * @throws NullPointerException if the map is null
      */
-    public static <K, V> SortedMap<K, V> lazySortedMap(final SortedMap<K, V> map,
-            final Transformer<? super K, ? extends V> transformerFactory) {
-        return LazySortedMap.lazySortedMap(map, transformerFactory);
+    public static <K, V> SortedMap<K, V> synchronizedSortedMap(final SortedMap<K, V> map) {
+        return Collections.synchronizedSortedMap(map);
     }
 
     /**
-     * Populates a Map using the supplied <code>Transformer</code> to transform the elements
-     * into keys, using the unaltered element as the value in the <code>Map</code>.
+     * Creates a new HashMap using data copied from a ResourceBundle.
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map the <code>Map</code> to populate.
-     * @param elements the <code>Iterable</code> containing the input values for the map.
-     * @param keyTransformer the <code>Transformer</code> used to transform the element into a key value
-     * @throws NullPointerException if the map, elements or transformer are null
+     * @param resourceBundle the resource bundle to convert, may not be null
+     * @return the hashmap containing the data
+     * @throws NullPointerException if the bundle is null
      */
-    public static <K, V> void populateMap(final Map<K, V> map, final Iterable<? extends V> elements,
-                                          final Transformer<V, K> keyTransformer) {
-        populateMap(map, elements, keyTransformer, TransformerUtils.<V>nopTransformer());
+    public static Map<String, Object> toMap(final ResourceBundle resourceBundle) {
+        final Enumeration<String> enumeration = resourceBundle.getKeys();
+        final Map<String, Object> map = new HashMap<>();
+
+        while (enumeration.hasMoreElements()) {
+            final String key = enumeration.nextElement();
+            final Object value = resourceBundle.getObject(key);
+            map.put(key, value);
+        }
+
+        return map;
     }
 
+    // -------------------------------------------------------------------------
     /**
-     * Populates a Map using the supplied <code>Transformer</code>s to transform the elements
-     * into keys and values.
+     * Gets a new Properties object initialised with the values from a Map. A null input will return an empty properties
+     * object.
+     * <p>
+     * A Properties object may only store non-null keys and values, thus if the provided map contains either a key or
+     * value which is {@code null}, a {@link NullPointerException} will be thrown.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param <E>  the type of object contained in the {@link Iterable}
-     * @param map the <code>Map</code> to populate.
-     * @param elements the <code>Iterable</code> containing the input values for the map.
-     * @param keyTransformer the <code>Transformer</code> used to transform the element into a key value
-     * @param valueTransformer the <code>Transformer</code> used to transform the element into a value
-     * @throws NullPointerException if the map, elements or transformers are null
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to convert to a Properties object
+     * @return the properties object
+     * @throws NullPointerException if a key or value in the provided map is {@code null}
      */
-    public static <K, V, E> void populateMap(final Map<K, V> map, final Iterable<? extends E> elements,
-                                             final Transformer<E, K> keyTransformer,
-                                             final Transformer<E, V> valueTransformer) {
-        final Iterator<? extends E> iter = elements.iterator();
-        while (iter.hasNext()) {
-            final E temp = iter.next();
-            map.put(keyTransformer.transform(temp), valueTransformer.transform(temp));
+    public static <K, V> Properties toProperties(final Map<K, V> map) {
+        final Properties answer = new Properties();
+        if (map != null) {
+            for (final Entry<K, V> entry2 : map.entrySet()) {
+                final Map.Entry<?, ?> entry = entry2;
+                final Object key = entry.getKey();
+                final Object value = entry.getValue();
+                answer.put(key, value);
+            }
         }
+        return answer;
     }
 
     /**
-     * Populates a MultiMap using the supplied <code>Transformer</code> to transform the elements
-     * into keys, using the unaltered element as the value in the <code>MultiMap</code>.
+     * Returns a transformed map backed by the given map.
+     * <p>
+     * This method returns a new map (decorating the specified map) that will transform any new entries added to it.
+     * Existing entries in the specified map will not be transformed. If you want that behaviour, see
+     * {@link TransformedMap#transformedMap}.
+     * </p>
+     * <p>
+     * Each object is passed through the transformers as it is added to the Map. It is important not to use the original
+     * map after invoking this method, as it is a backdoor for adding untransformed objects.
+     * </p>
+     * <p>
+     * If there are any elements already in the map being decorated, they are NOT transformed.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map the <code>MultiMap</code> to populate.
-     * @param elements the <code>Iterable</code> to use as input values for the map.
-     * @param keyTransformer the <code>Transformer</code> used to transform the element into a key value
-     * @throws NullPointerException if the map, elements or transformer are null
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to transform, must not be null, typically empty
+     * @param keyTransformer the transformer for the map keys, null means no transformation
+     * @param valueTransformer the transformer for the map values, null means no transformation
+     * @return a transformed map backed by the given map
+     * @throws NullPointerException if the Map is null
      */
-    public static <K, V> void populateMap(final MultiMap<K, V> map, final Iterable<? extends V> elements,
-                                          final Transformer<V, K> keyTransformer) {
-        populateMap(map, elements, keyTransformer, TransformerUtils.<V>nopTransformer());
+    public static <K, V> IterableMap<K, V> transformedMap(final Map<K, V> map,
+            final Transformer<? super K, ? extends K> keyTransformer,
+            final Transformer<? super V, ? extends V> valueTransformer) {
+        return TransformedMap.transformingMap(map, keyTransformer, valueTransformer);
     }
 
     /**
-     * Populates a MultiMap using the supplied <code>Transformer</code>s to transform the elements
-     * into keys and values.
+     * Returns a transformed sorted map backed by the given map.
+     * <p>
+     * This method returns a new sorted map (decorating the specified map) that will transform any new entries added to
+     * it. Existing entries in the specified map will not be transformed. If you want that behaviour, see
+     * {@link TransformedSortedMap#transformedSortedMap}.
+     * </p>
+     * <p>
+     * Each object is passed through the transformers as it is added to the Map. It is important not to use the original
+     * map after invoking this method, as it is a backdoor for adding untransformed objects.
+     * </p>
+     * <p>
+     * If there are any elements already in the map being decorated, they are NOT transformed.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param <E>  the type of object contained in the {@link Iterable}
-     * @param map the <code>MultiMap</code> to populate.
-     * @param elements the <code>Iterable</code> containing the input values for the map.
-     * @param keyTransformer the <code>Transformer</code> used to transform the element into a key value
-     * @param valueTransformer the <code>Transformer</code> used to transform the element into a value
-     * @throws NullPointerException if the map, collection or transformers are null
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to transform, must not be null, typically empty
+     * @param keyTransformer the transformer for the map keys, null means no transformation
+     * @param valueTransformer the transformer for the map values, null means no transformation
+     * @return a transformed map backed by the given map
+     * @throws NullPointerException if the SortedMap is null
      */
-    public static <K, V, E> void populateMap(final MultiMap<K, V> map, final Iterable<? extends E> elements,
-                                             final Transformer<E, K> keyTransformer,
-                                             final Transformer<E, V> valueTransformer) {
-        final Iterator<? extends E> iter = elements.iterator();
-        while (iter.hasNext()) {
-            final E temp = iter.next();
-            map.put(keyTransformer.transform(temp), valueTransformer.transform(temp));
-        }
+    public static <K, V> SortedMap<K, V> transformedSortedMap(final SortedMap<K, V> map,
+            final Transformer<? super K, ? extends K> keyTransformer,
+            final Transformer<? super V, ? extends V> valueTransformer) {
+        return TransformedSortedMap.transformingSortedMap(map, keyTransformer, valueTransformer);
     }
 
     /**
-     * Get the specified {@link Map} as an {@link IterableMap}.
+     * Returns an unmodifiable map backed by the given map.
+     * <p>
+     * This method uses the implementation in the decorators subpackage.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param map to wrap if necessary.
-     * @return IterableMap&lt;K, V&gt;
-     * @throws NullPointerException if map is null
-     * @since 4.0
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the map to make unmodifiable, must not be null
+     * @return an unmodifiable map backed by the given map
+     * @throws NullPointerException if the map is null
      */
-    public static <K, V> IterableMap<K, V> iterableMap(final Map<K, V> map) {
-        if (map == null) {
-            throw new NullPointerException("Map must not be null");
-        }
-        return map instanceof IterableMap ? (IterableMap<K, V>) map : new AbstractMapDecorator<K, V>(map) {};
+    public static <K, V> Map<K, V> unmodifiableMap(final Map<? extends K, ? extends V> map) {
+        return UnmodifiableMap.unmodifiableMap(map);
     }
 
     /**
-     * Get the specified {@link SortedMap} as an {@link IterableSortedMap}.
+     * Returns an unmodifiable sorted map backed by the given sorted map.
+     * <p>
+     * This method uses the implementation in the decorators subpackage.
+     * </p>
      *
-     * @param <K>  the key type
-     * @param <V>  the value type
-     * @param sortedMap to wrap if necessary
-     * @return {@link IterableSortedMap}&lt;K, V&gt;
-     * @throws NullPointerException if sortedMap is null
-     * @since 4.0
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param map the sorted map to make unmodifiable, must not be null
+     * @return an unmodifiable map backed by the given map
+     * @throws NullPointerException if the map is null
      */
-    public static <K, V> IterableSortedMap<K, V> iterableSortedMap(final SortedMap<K, V> sortedMap) {
-        if (sortedMap == null) {
-            throw new NullPointerException("Map must not be null");
+    public static <K, V> SortedMap<K, V> unmodifiableSortedMap(final SortedMap<K, ? extends V> map) {
+        return UnmodifiableSortedMap.unmodifiableSortedMap(map);
+    }
+
+    // Printing methods
+    // -------------------------------------------------------------------------
+    /**
+     * Prints the given map with nice line breaks.
+     * <p>
+     * This method prints a nicely formatted String describing the Map. Each map entry will be printed with key and
+     * value. When the value is a Map, recursive behaviour occurs.
+     * </p>
+     * <p>
+     * This method is NOT thread-safe in any special way. You must manually synchronize on either this class or the
+     * stream as required.
+     * </p>
+     *
+     * @param out the stream to print to, must not be null
+     * @param label The label to be used, may be <code>null</code>. If <code>null</code>, the label is not output. It
+     *        typically represents the name of the property in a bean or similar.
+     * @param map The map to print, may be <code>null</code>. If <code>null</code>, the text 'null' is output.
+     * @throws NullPointerException if the stream is <code>null</code>
+     */
+    public static void verbosePrint(final PrintStream out, final Object label, final Map<?, ?> map) {
+        verbosePrintInternal(out, label, map, new ArrayDeque<Map<?, ?>>(), false);
+    }
+
+    /**
+     * Implementation providing functionality for {@link #debugPrint} and for {@link #verbosePrint}. This prints the
+     * given map with nice line breaks. If the debug flag is true, it additionally prints the type of the object value.
+     * If the contents of a map include the map itself, then the text <em>(this Map)</em> is printed out. If the
+     * contents include a parent container of the map, the text <em>(ancestor[i] Map)</em> is printed, where i actually
+     * indicates the number of levels which must be traversed in the sequential list of ancestors (e.g. father,
+     * grandfather, great-grandfather, etc).
+     *
+     * @param out the stream to print to
+     * @param label the label to be used, may be <code>null</code>. If <code>null</code>, the label is not output. It
+     *        typically represents the name of the property in a bean or similar.
+     * @param map the map to print, may be <code>null</code>. If <code>null</code>, the text 'null' is output
+     * @param lineage a stack consisting of any maps in which the previous argument is contained. This is checked to
+     *        avoid infinite recursion when printing the output
+     * @param debug flag indicating whether type names should be output.
+     * @throws NullPointerException if the stream is <code>null</code>
+     */
+    private static void verbosePrintInternal(final PrintStream out, final Object label, final Map<?, ?> map,
+            final Deque<Map<?, ?>> lineage, final boolean debug) {
+        printIndent(out, lineage.size());
+
+        if (map == null) {
+            if (label != null) {
+                out.print(label);
+                out.print(" = ");
+            }
+            out.println("null");
+            return;
+        }
+        if (label != null) {
+            out.print(label);
+            out.println(" = ");
         }
-        return sortedMap instanceof IterableSortedMap ? (IterableSortedMap<K, V>) sortedMap :
-                                                        new AbstractSortedMapDecorator<K, V>(sortedMap) {};
+
+        printIndent(out, lineage.size());
+        out.println("{");
+
+        lineage.addLast(map);
+
+        for (final Map.Entry<?, ?> entry : map.entrySet()) {
+            final Object childKey = entry.getKey();
+            final Object childValue = entry.getValue();
+            if (childValue instanceof Map && !lineage.contains(childValue)) {
+                verbosePrintInternal(out, childKey == null ? "null" : childKey, (Map<?, ?>) childValue, lineage, debug);
+            } else {
+                printIndent(out, lineage.size());
+                out.print(childKey);
+                out.print(" = ");
+
+                final int lineageIndex = IterableUtils.indexOf(lineage, PredicateUtils.equalPredicate(childValue));
+                if (lineageIndex == -1) {
+                    out.print(childValue);
+                } else if (lineage.size() - 1 == lineageIndex) {
+                    out.print("(this Map)");
+                } else {
+                    out.print("(ancestor[" + (lineage.size() - 1 - lineageIndex - 1) + "] Map)");
+                }
+
+                if (debug && childValue != null) {
+                    out.print(' ');
+                    out.println(childValue.getClass().getName());
+                } else {
+                    out.println();
+                }
+            }
+        }
+
+        lineage.removeLast();
+
+        printIndent(out, lineage.size());
+        out.println(debug ? "} " + map.getClass().getName() : "}");
     }
 
     /**
-     * Gets the given map size or 0 if the map is null
-     * @param map a Map or null
-     * @return the given map size or 0 if the map is null
+     * <code>MapUtils</code> should not normally be instantiated.
      */
-    public static int size(final Map<?, ?> map) {
-        return map == null ? 0 : map.size();
+    private MapUtils() {
     }
 
 }
diff --git a/src/test/java/org/apache/commons/collections4/MapUtilsTest.java b/src/test/java/org/apache/commons/collections4/MapUtilsTest.java
index 676a3ab..9a2470c 100644
--- a/src/test/java/org/apache/commons/collections4/MapUtilsTest.java
+++ b/src/test/java/org/apache/commons/collections4/MapUtilsTest.java
@@ -39,6 +39,7 @@ import java.util.Properties;
 import java.util.ResourceBundle;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.function.Function;
 
 import org.apache.commons.collections4.collection.TransformedCollectionTest;
 import org.apache.commons.collections4.junit.AbstractAvailableLocalesTest;
@@ -182,9 +183,9 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
 
         // sub array
         test = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
-            {"RED", "#FF0000"},
-            {"GREEN", "#00FF00"},
-            {"BLUE", "#0000FF"}
+                {"RED", "#FF0000"},
+                {"GREEN", "#00FF00"},
+                {"BLUE", "#0000FF"}
         });
         assertEquals(true, test.containsKey("RED"));
         assertEquals("#FF0000", test.get("RED"));
@@ -196,36 +197,36 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
 
         try {
             MapUtils.putAll(new HashMap<String, String>(), new String[][] {
-                {"RED", "#FF0000"},
-                null,
-                {"BLUE", "#0000FF"}
+                    {"RED", "#FF0000"},
+                    null,
+                    {"BLUE", "#0000FF"}
             });
             fail();
         } catch (final IllegalArgumentException ex) {}
 
         try {
             MapUtils.putAll(new HashMap<String, String>(), new String[][] {
-                {"RED", "#FF0000"},
-                {"GREEN"},
-                {"BLUE", "#0000FF"}
+                    {"RED", "#FF0000"},
+                    {"GREEN"},
+                    {"BLUE", "#0000FF"}
             });
             fail();
         } catch (final IllegalArgumentException ex) {}
 
         try {
             MapUtils.putAll(new HashMap<String, String>(), new String[][] {
-                {"RED", "#FF0000"},
-                {},
-                {"BLUE", "#0000FF"}
+                    {"RED", "#FF0000"},
+                    {},
+                    {"BLUE", "#0000FF"}
             });
             fail();
         } catch (final IllegalArgumentException ex) {}
 
         // flat array
         test = MapUtils.putAll(new HashMap<String, String>(), new String[] {
-            "RED", "#FF0000",
-            "GREEN", "#00FF00",
-            "BLUE", "#0000FF"
+                "RED", "#FF0000",
+                "GREEN", "#00FF00",
+                "BLUE", "#0000FF"
         });
         assertEquals(true, test.containsKey("RED"));
         assertEquals("#FF0000", test.get("RED"));
@@ -236,10 +237,10 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
         assertEquals(3, test.size());
 
         test = MapUtils.putAll(new HashMap<String, String>(), new String[] {
-            "RED", "#FF0000",
-            "GREEN", "#00FF00",
-            "BLUE", "#0000FF",
-            "PURPLE" // ignored
+                "RED", "#FF0000",
+                "GREEN", "#00FF00",
+                "BLUE", "#0000FF",
+                "PURPLE" // ignored
         });
         assertEquals(true, test.containsKey("RED"));
         assertEquals("#FF0000", test.get("RED"));
@@ -254,9 +255,9 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
 
         // map entry
         test = MapUtils.putAll(new HashMap<String, String>(), new Object[] {
-            new DefaultMapEntry<>("RED", "#FF0000"),
-            new DefaultMapEntry<>("GREEN", "#00FF00"),
-            new DefaultMapEntry<>("BLUE", "#0000FF")
+                new DefaultMapEntry<>("RED", "#FF0000"),
+                new DefaultMapEntry<>("GREEN", "#00FF00"),
+                new DefaultMapEntry<>("BLUE", "#0000FF")
         });
         assertEquals(true, test.containsKey("RED"));
         assertEquals("#FF0000", test.get("RED"));
@@ -268,9 +269,9 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
 
         // key value
         test = MapUtils.putAll(new HashMap<String, String>(), new Object[] {
-            new DefaultKeyValue<>("RED", "#FF0000"),
-            new DefaultKeyValue<>("GREEN", "#00FF00"),
-            new DefaultKeyValue<>("BLUE", "#0000FF")
+                new DefaultKeyValue<>("RED", "#FF0000"),
+                new DefaultKeyValue<>("GREEN", "#00FF00"),
+                new DefaultKeyValue<>("BLUE", "#0000FF")
         });
         assertEquals(true, test.containsKey("RED"));
         assertEquals("#FF0000", test.get("RED"));
@@ -953,9 +954,17 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
         assertEquals(2.0, MapUtils.getDoubleValue(in,"key", 0.0), 0);
         assertEquals(2.0, MapUtils.getDoubleValue(in,"key"), 0);
         assertEquals(1.0, MapUtils.getDoubleValue(in,"noKey", 1.0), 0);
+        assertEquals(5.0, MapUtils.getDoubleValue(in,"noKey", (key)->{
+            //sometimes the default value need to be calculated,such as System.currentTimeMillis()
+            return 5.0D;
+        }),0);
+
         assertEquals(0, MapUtils.getDoubleValue(in,"noKey"), 0);
         assertEquals(2.0, MapUtils.getDouble(in,"key", 0.0), 0);
         assertEquals(1.0, MapUtils.getDouble(in,"noKey", 1.0), 0);
+        assertEquals(1.0, MapUtils.getDouble(in,"noKey", (key)->{
+            return 1.0;
+        }), 0);
 
 
         final Map<String, String> inStr = new HashMap<>();
@@ -973,9 +982,15 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
         assertEquals(2.0, MapUtils.getFloatValue(in,"key", 0.0f), 0);
         assertEquals(2.0, MapUtils.getFloatValue(in,"key"), 0);
         assertEquals(1.0, MapUtils.getFloatValue(in,"noKey", 1.0f), 0);
+        assertEquals(1.0, MapUtils.getFloatValue(in,"noKey", (key)->{
+            return 1.0F;
+        }), 0);
         assertEquals(0, MapUtils.getFloatValue(in,"noKey"), 0);
         assertEquals(2.0, MapUtils.getFloat(in,"key", 0.0f), 0);
         assertEquals(1.0, MapUtils.getFloat(in,"noKey", 1.0f), 0);
+        assertEquals(1.0, MapUtils.getFloat(in,"noKey", (key)->{
+            return 1.0F;
+        }), 0);
 
         final Map<String, String> inStr = new HashMap<>();
         final char decimalSeparator = getDecimalSeparator();
@@ -992,9 +1007,15 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
         assertEquals(2.0, MapUtils.getLongValue(in,"key", 0L), 0);
         assertEquals(2.0, MapUtils.getLongValue(in,"key"), 0);
         assertEquals(1, MapUtils.getLongValue(in,"noKey", 1L), 0);
+        assertEquals(1, MapUtils.getLongValue(in,"noKey", (key)->{
+            return 1L;
+        }), 0);
         assertEquals(0, MapUtils.getLongValue(in,"noKey"), 0);
         assertEquals(2.0, MapUtils.getLong(in,"key", 0L), 0);
         assertEquals(1, MapUtils.getLong(in,"noKey", 1L), 0);
+        assertEquals(1, MapUtils.getLong(in,"noKey", (key)->{
+            return 1L;
+        }), 0);
 
         final Map<String, String> inStr = new HashMap<>();
         inStr.put("str1", "2");
@@ -1012,9 +1033,15 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
         assertEquals(2, MapUtils.getIntValue(in,"key", 0), 0);
         assertEquals(2, MapUtils.getIntValue(in,"key"), 0);
         assertEquals(0, MapUtils.getIntValue(in,"noKey", 0), 0);
+        assertEquals(0, MapUtils.getIntValue(in,"noKey", (key)->{
+            return 0;
+        }), 0);
         assertEquals(0, MapUtils.getIntValue(in,"noKey"), 0);
         assertEquals(2, MapUtils.getInteger(in,"key", 0), 0);
         assertEquals(0, MapUtils.getInteger(in,"noKey", 0), 0);
+        assertEquals(0, MapUtils.getInteger(in,"noKey", (key)->{
+            return 0;
+        }), 0);
 
         final Map<String, String> inStr = new HashMap<>();
         inStr.put("str1", "2");
@@ -1031,9 +1058,15 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
         assertEquals(val, MapUtils.getShortValue(in,"key", val), 0);
         assertEquals(val, MapUtils.getShortValue(in,"key"), 0);
         assertEquals(val, MapUtils.getShortValue(in,"noKey", val), 0);
+        assertEquals(val, MapUtils.getShortValue(in,"noKey", (key)->{
+            return val;
+        }), 0);
         assertEquals(0, MapUtils.getShortValue(in,"noKey"), 0);
         assertEquals(val, MapUtils.getShort(in,"key", val), 0);
         assertEquals(val,MapUtils.getShort(in,"noKey", val), 0);
+        assertEquals(val,MapUtils.getShort(in,"noKey", (key)->{
+            return val;
+        }), 0);
 
         final Map<String, String> inStr = new HashMap<>();
         inStr.put("str1", "10");
@@ -1050,9 +1083,15 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
         assertEquals(val, MapUtils.getByteValue(in,"key", val), 0);
         assertEquals(val, MapUtils.getByteValue(in,"key"), 0);
         assertEquals(val, MapUtils.getByteValue(in,"noKey", val), 0);
+        assertEquals(val, MapUtils.getByteValue(in,"noKey", (key)->{
+            return (byte)100;
+        }), 0);
         assertEquals(0, MapUtils.getByteValue(in,"noKey"), 0);
         assertEquals(val, MapUtils.getByte(in,"key", val), 0);
         assertEquals(val, MapUtils.getByte(in,"noKey", val), 0);
+        assertEquals(val, MapUtils.getByte(in,"noKey", (key)->{
+            return val;
+        }), 0);
 
 
         final Map<String, String> inStr = new HashMap<>();
@@ -1069,6 +1108,13 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
 
         assertEquals(val.intValue(), MapUtils.getNumber(in,"key", val).intValue(), 0);
         assertEquals(val.intValue(), MapUtils.getNumber(in,"noKey", val).intValue(), 0);
+        assertEquals(val.intValue(), MapUtils.getNumber(in,"noKey", (key)->{
+            if (true) {
+                return val;
+            } else {
+                return null;
+            }
+        }).intValue(), 0);
 
     }
 
@@ -1081,6 +1127,13 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
         assertEquals("str", MapUtils.getString(in,"key"));
         assertEquals(null, MapUtils.getString(null,"key"));
         assertEquals("default", MapUtils.getString(in,"noKey", "default"));
+        assertEquals("default", MapUtils.getString(in,"noKey", (key)->{
+            if ("noKey".equals(key)) {
+                return "default";
+            } else {
+                return "";
+            }
+        }));
         assertEquals("default", MapUtils.getString(null,"noKey", "default"));
 
     }
@@ -1095,7 +1148,6 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
         assertEquals(null, MapUtils.getObject(null,"key"));
         assertEquals("default", MapUtils.getObject(in,"noKey", "default"));
         assertEquals("default", MapUtils.getObject(null,"noKey", "default"));
-
     }
 
     @Test
@@ -1106,9 +1158,19 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
         assertTrue(MapUtils.getBooleanValue(in,"key", true));
         assertTrue(MapUtils.getBooleanValue(in,"key"));
         assertTrue(MapUtils.getBooleanValue(in,"noKey", true));
+        assertTrue(MapUtils.getBooleanValue(in,"noKey", (key)->{
+            return true;
+        }));
         assertTrue(!MapUtils.getBooleanValue(in,"noKey"));
         assertTrue(MapUtils.getBoolean(in,"key", true));
         assertTrue(MapUtils.getBoolean(in,"noKey", true));
+        assertTrue(MapUtils.getBoolean(in,"noKey", (key)->{
+            if (System.currentTimeMillis() > 0) {
+                return true;
+            } else {
+                return false;
+            }
+        }));
         assertEquals(null, MapUtils.getBoolean(null,"noKey"));
 
 
@@ -1128,12 +1190,12 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
         final Map<String, String> valMap = new HashMap<>();
         valMap.put("key1", "value1");
         in.put("key1", valMap);
-        final Map<?, ?> outValue =  MapUtils.getMap(in,"key1", null);
+        final Map<?, ?> outValue =  MapUtils.getMap(in,"key1", (Map<?, ?>) null);
 
         assertEquals("value1", outValue.get("key1"));
         assertEquals(null, outValue.get("key2"));
-        assertEquals(null, MapUtils.getMap(in,"key2", null));
-        assertEquals(null, MapUtils.getMap(null,"key2", null));
+        assertEquals(null, MapUtils.getMap(in, "key2", (Map<?, ?>) null));
+        assertEquals(null, MapUtils.getMap(null, "key2", (Map<?, ?>) null));
     }
 
     @Test
@@ -1149,9 +1211,9 @@ public class MapUtilsTest extends AbstractAvailableLocalesTest {
 
     @Test
     public void testOrderedMap() {
-    	final Map<String, String> inMap = new HashMap<>();
-    	inMap.put("key1", "value1");
-    	inMap.put("key2", "value2");
+        final Map<String, String> inMap = new HashMap<>();
+        inMap.put("key1", "value1");
+        inMap.put("key2", "value2");
         final Map<String, String> map = MapUtils.orderedMap(inMap);
         assertTrue("returned object should be a OrderedMap", map instanceof OrderedMap);
     }