diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 1e4db0937..9084d14bf 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -22,6 +22,9 @@
+
+ Added "DualLinkedHashBidiMap" bidi map implementation.
+
Added "LazyIteratorChain" iterator.
diff --git a/src/main/java/org/apache/commons/collections/bidimap/DualLinkedHashBidiMap.java b/src/main/java/org/apache/commons/collections/bidimap/DualLinkedHashBidiMap.java
new file mode 100644
index 000000000..16a865fda
--- /dev/null
+++ b/src/main/java/org/apache/commons/collections/bidimap/DualLinkedHashBidiMap.java
@@ -0,0 +1,99 @@
+/*
+ * 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.bidimap;
+
+import org.apache.commons.collections.BidiMap;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Implementation of BidiMap
that uses two LinkedHashMap
instances.
+ *
+ * Two LinkedHashMap
instances are used in this class.
+ * This provides fast lookups at the expense of storing two sets of map entries and two linked lists.
+ *
+ * @version $Id $
+ * @since 4.0
+ */
+public class DualLinkedHashBidiMap extends AbstractDualBidiMap implements Serializable {
+
+ /** Ensure serialization compatibility */
+ private static final long serialVersionUID = 721969328361810L;
+
+ /**
+ * Creates an empty HashBidiMap
.
+ */
+ public DualLinkedHashBidiMap() {
+ super(new LinkedHashMap(), new LinkedHashMap());
+ }
+
+ /**
+ * Constructs a LinkedHashBidiMap
and copies the mappings from
+ * specified Map
.
+ *
+ * @param map the map whose mappings are to be placed in this map
+ */
+ public DualLinkedHashBidiMap(Map map) {
+ super(new LinkedHashMap(), new LinkedHashMap());
+ putAll(map);
+ }
+
+ /**
+ * Constructs a LinkedHashBidiMap
that decorates the specified maps.
+ *
+ * @param normalMap the normal direction map
+ * @param reverseMap the reverse direction map
+ * @param inverseBidiMap the inverse BidiMap
+ */
+ protected DualLinkedHashBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {
+ super(normalMap, reverseMap, inverseBidiMap);
+ }
+
+ /**
+ * Creates a new instance of this object.
+ *
+ * @param normalMap the normal direction map
+ * @param reverseMap the reverse direction map
+ * @param inverseBidiMap the inverse BidiMap
+ * @return new bidi map
+ */
+ protected BidiMap createBidiMap(final Map normalMap, final Map reverseMap,
+ final BidiMap inverseBidiMap) {
+ return new DualLinkedHashBidiMap(normalMap, reverseMap, inverseBidiMap);
+ }
+
+ // Serialization
+ //-----------------------------------------------------------------------
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ out.writeObject(normalMap);
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ normalMap = new LinkedHashMap();
+ reverseMap = new LinkedHashMap();
+ @SuppressWarnings("unchecked") // will fail at runtime if stream is incorrect
+ Map map = (Map) in.readObject();
+ putAll(map);
+ }
+}
diff --git a/src/test/java/org/apache/commons/collections/bidimap/DualLinkedHashBidiMapTest.java b/src/test/java/org/apache/commons/collections/bidimap/DualLinkedHashBidiMapTest.java
new file mode 100644
index 000000000..316a8d4f5
--- /dev/null
+++ b/src/test/java/org/apache/commons/collections/bidimap/DualLinkedHashBidiMapTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.bidimap;
+
+import junit.framework.Test;
+import org.apache.commons.collections.BulkTest;
+
+/**
+ * JUnit tests.
+ *
+ * @version $Id $
+ */
+public class DualLinkedHashBidiMapTest extends AbstractBidiMapTest {
+
+ public static Test suite() {
+ return BulkTest.makeSuite(DualLinkedHashBidiMapTest.class);
+ }
+
+ public DualLinkedHashBidiMapTest(final String testName) {
+ super(testName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DualLinkedHashBidiMap makeObject() {
+ return new DualLinkedHashBidiMap();
+ }
+
+ @Override
+ public String getCompatibilityVersion() {
+ return "4";
+ }
+
+ /**
+ * Override to prevent infinite recursion of tests.
+ */
+ @Override
+ public String[] ignoredTests() {
+ return new String[] { "DualLinkedHashBidiMapTest.bulkTestInverseMap.bulkTestInverseMap" };
+ }
+
+// public void testCreate() throws Exception {
+// resetEmpty();
+// writeExternalFormToDisk((Serializable) map, "src/test/resources/data/test/DualLinkedHashBidiMap.emptyCollection.version4.obj");
+// resetFull();
+// writeExternalFormToDisk((Serializable) map, "src/test/resources/data/test/DualLinkedHashBidiMap.fullCollection.version4.obj");
+// }
+}
diff --git a/src/test/resources/data/test/DualLinkedHashBidiMap.emptyCollection.version4.obj b/src/test/resources/data/test/DualLinkedHashBidiMap.emptyCollection.version4.obj
new file mode 100644
index 000000000..91074a9df
Binary files /dev/null and b/src/test/resources/data/test/DualLinkedHashBidiMap.emptyCollection.version4.obj differ
diff --git a/src/test/resources/data/test/DualLinkedHashBidiMap.fullCollection.version4.obj b/src/test/resources/data/test/DualLinkedHashBidiMap.fullCollection.version4.obj
new file mode 100644
index 000000000..bc38b6943
Binary files /dev/null and b/src/test/resources/data/test/DualLinkedHashBidiMap.fullCollection.version4.obj differ