diff --git a/src/main/java/org/apache/commons/collections/map/PassiveExpiringMap.java b/src/main/java/org/apache/commons/collections/map/PassiveExpiringMap.java
new file mode 100644
index 000000000..c0184cffa
--- /dev/null
+++ b/src/main/java/org/apache/commons/collections/map/PassiveExpiringMap.java
@@ -0,0 +1,577 @@
+/*
+ * 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.
+ */
+package org.apache.commons.collections.map;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Decorates a Map to evict expired entries once their expiration
+ * time has been reached.
+ *
+ * When putting a key-value pair in the map this decorator uses a
+ * {@link ExpirationPolicy} to determine how long the entry should remain alive
+ * as defined by an expiration time value.
+ *
+ *
+ * When accessing the mapped value for a key, its expiration time is checked,
+ * and if it is a negative value or if it is greater than the current time, the
+ * mapped value is returned. Otherwise, the key is removed from the decorated
+ * map, and null is returned.
+ *
+ *
+ * When invoking methods that involve accessing the entire map contents (i.e
+ * {@link #containsKey(Object)}, {@link #entrySet()}, etc.) this decorator
+ * removes all expired entries prior to actually completing the invocation.
+ *
+ *
+ * Note that {@link PassiveExpiringMap} is not synchronized and is not
+ * thread-safe. If you wish to use this map from multiple threads
+ * concurrently, you must use appropriate synchronization. The simplest approach
+ * is to wrap this map using {@link java.util.Collections#synchronizedMap(Map)}.
+ * This class may throw exceptions when accessed by concurrent threads without
+ * synchronization.
+ *
+ *
+ * @param
+ * the type of the keys in the map
+ *
+ * @param
+ * the type of the values in the map
+ *
+ * @since 4.0
+ * @version $Id: $
+ */
+public class PassiveExpiringMap extends AbstractMapDecorator
+ implements Serializable {
+
+ /**
+ * A {@link ExpirationPolicy} that returns a expiration time that is a
+ * constant about of time in the future from the current time.
+ *
+ * @param
+ * the type of the keys in the map
+ * @param
+ * the type of the values in the map
+ *
+ * @since 4.0
+ * @version $Id: $
+ */
+ public static class ConstantTimeToLiveExpirationPolicy implements
+ ExpirationPolicy {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ /** the constant time-to-live value measured in milliseconds. */
+ private final long timeToLiveMillis;
+
+ /**
+ * Default constructor. Constructs a policy using a negative
+ * time-to-live value that results in entries never expiring.
+ */
+ public ConstantTimeToLiveExpirationPolicy() {
+ this(-1L);
+ }
+
+ /**
+ * Construct a policy with the given time-to-live constant measured in
+ * milliseconds. A negative time-to-live value indicates entries never
+ * expire. A zero time-to-live value indicates entries expire (nearly)
+ * immediately.
+ *
+ * @param timeToLiveMillis
+ * the constant amount of time (in milliseconds) an entry is
+ * available before it expires. A negative value results in
+ * entries that NEVER expire. A zero value results in entries
+ * that ALWAYS expire.
+ */
+ public ConstantTimeToLiveExpirationPolicy(long timeToLiveMillis) {
+ super();
+ this.timeToLiveMillis = timeToLiveMillis;
+ }
+
+ /**
+ * Construct a policy with the given time-to-live constant measured in
+ * the given time unit of measure.
+ *
+ * @param timeToLive
+ * the constant amount of time an entry is available before
+ * it expires. A negative value results in entries that NEVER
+ * expire. A zero value results in entries that ALWAYS
+ * expire.
+ * @param timeUnit
+ * the unit of time for the timeToLive
+ * parameter, must not be null.
+ * @throws IllegalArgumentException
+ * if the time unit is null.
+ */
+ public ConstantTimeToLiveExpirationPolicy(long timeToLive,
+ TimeUnit timeUnit) {
+ this(validateAndConvertToMillis(timeToLive, TimeUnit.MILLISECONDS));
+ }
+
+ /**
+ * Determine the expiration time for the given key-value entry.
+ *
+ * @param key
+ * the key for the entry (ignored).
+ * @param value
+ * the value for the entry (ignored).
+ * @return if {@link #timeToLiveMillis} ≥ 0, an expiration time of
+ * {@link #timeToLiveMillis} +
+ * {@link System#currentTimeMillis()} is returned. Otherwise, -1
+ * is returned indicating the entry never expires.
+ */
+ public long expirationTime(K key, V value) {
+ if (timeToLiveMillis >= 0L) {
+ // avoid numerical overflow
+ long now = System.currentTimeMillis();
+ if (now > Long.MAX_VALUE - timeToLiveMillis) {
+ // expiration would be greater than Long.MAX_VALUE
+ // never expire
+ return -1;
+ }
+
+ // timeToLiveMillis in the future
+ return now + timeToLiveMillis;
+ }
+
+ // never expire
+ return -1L;
+ }
+ }
+
+ /**
+ * A policy to determine the expiration time for key-value entries.
+ *
+ * @param
+ * the key object type.
+ * @param
+ * the value object type
+ *
+ * @since 4.0
+ * @version $Id: $
+ */
+ public static interface ExpirationPolicy extends Serializable {
+ /**
+ * Determine the expiration time for the given key-value entry.
+ *
+ * @param key
+ * the key for the entry.
+ * @param value
+ * the value for the entry.
+ * @return the expiration time value measured in milliseconds. A
+ * negative return value indicates the entry never expires.
+ */
+ long expirationTime(K key, V value);
+ }
+
+ /** Serialization version */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * First validate the input parameters. If the parameters are valid, convert
+ * the given time measured in the given units to the same time measured in
+ * milliseconds. If the parameters are invalid, an
+ * {@link IllegalArgumentException} is thrown.
+ *
+ * @param timeToLive
+ * the constant amount of time an entry is available before it
+ * expires. A negative value results in entries that NEVER
+ * expire. A zero value results in entries that ALWAYS expire.
+ * @param timeUnit
+ * the unit of time for the timeToLive parameter,
+ * must not be null.
+ * @throws IllegalArgumentException
+ * if the time unit is null.
+ */
+ private static long validateAndConvertToMillis(long timeToLive,
+ TimeUnit timeUnit) {
+ if (timeUnit == null) {
+ throw new IllegalArgumentException("Time unit must not be null");
+ }
+ return timeUnit.convert(timeToLive, TimeUnit.MILLISECONDS);
+ }
+
+ /** map used to manage expiration times for the actual map entries. */
+ private final Map