Skip to content

Commit 45f507e

Browse files
committed
Merge pull request functionaljava#26 from youdevise/improve-hashmap
Improve HashMap with #map, #mapValues, #mapKeys, #foreach
2 parents 427a4c4 + 427bca7 commit 45f507e

File tree

2 files changed

+118
-9
lines changed

2 files changed

+118
-9
lines changed

core/src/main/java/fj/data/HashMap.java

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package fj.data;
22

3-
import fj.Equal;
4-
import fj.F;
5-
import fj.Hash;
6-
import fj.P2;
3+
import fj.*;
74

85
import java.util.Collection;
96
import java.util.Iterator;
@@ -69,6 +66,13 @@ public HashMap(final Equal<K> e, final Hash<K> h) {
6966
this.h = h;
7067
}
7168

69+
public HashMap(java.util.Map<K, V> map, final Equal<K> e, final Hash<K> h) {
70+
this(e, h);
71+
for (K key : map.keySet()) {
72+
set(key, map.get(key));
73+
}
74+
}
75+
7276
/**
7377
* Construct a hash map with the given equality and hashing strategy.
7478
*
@@ -82,7 +86,11 @@ public HashMap(final Equal<K> e, final Hash<K> h, final int initialCapacity) {
8286
this.h = h;
8387
}
8488

85-
/**
89+
public HashMap(java.util.Map<K, V> map) {
90+
this(map, Equal.<K>anyEqual(), Hash.<K>anyHash());
91+
}
92+
93+
/**
8694
* Construct a hash map with the given equality and hashing strategy.
8795
*
8896
* @param e The equality strategy.
@@ -221,7 +229,9 @@ public int size() {
221229
* @param v The value to insert.
222230
*/
223231
public void set(final K k, final V v) {
224-
m.put(new Key<K>(k, e, h), v);
232+
if (v != null) {
233+
m.put(new Key<K>(k, e, h), v);
234+
}
225235
}
226236

227237
/**
@@ -243,6 +253,51 @@ public Option<V> getDelete(final K k) {
243253
return fromNull(m.remove(new Key<K>(k, e, h)));
244254
}
245255

256+
public <A, B> HashMap<A, B> map(F<K, A> keyFunction,
257+
F<V, B> valueFunction,
258+
Equal<A> equal, Hash<A> hash) {
259+
final HashMap<A, B> hashMap = new HashMap<A, B>(equal, hash);
260+
for (K key : keys()) {
261+
final A newKey = keyFunction.f(key);
262+
final B newValue = valueFunction.f(get(key).some());
263+
hashMap.set(newKey, newValue);
264+
}
265+
return hashMap;
266+
}
267+
268+
public <A, B> HashMap<A, B> map(F<K, A> keyFunction,
269+
F<V, B> valueFunction) {
270+
return map(keyFunction, valueFunction, Equal.<A>anyEqual(), Hash.<A>anyHash());
271+
}
272+
273+
public <A, B> HashMap<A, B> map(F<P2<K, V>, P2<A, B>> function, Equal<A> equal, Hash<A> hash) {
274+
return from(toStream().map(function), equal, hash);
275+
}
276+
277+
public <A, B> HashMap<A, B> map(F<P2<K, V>, P2<A, B>> function) {
278+
return from(toStream().map(function));
279+
}
280+
281+
public <A> HashMap<A, V> mapKeys(F<K, A> keyFunction, Equal<A> equal, Hash<A> hash) {
282+
return map(keyFunction, Function.<V>identity(), equal, hash);
283+
}
284+
285+
public <A> HashMap<A, V> mapKeys(F<K, A> function) {
286+
return mapKeys(function, Equal.<A>anyEqual(), Hash.<A>anyHash());
287+
}
288+
289+
public <B> HashMap<K, B> mapValues(F<V, B> function) {
290+
return map(Function.<K>identity(), function, e, h);
291+
}
292+
293+
public void foreach(Effect<P2<K, V>> effect) {
294+
toStream().foreach(effect);
295+
}
296+
297+
public void foreach(F<P2<K, V>, Unit> function) {
298+
toStream().foreach(function);
299+
}
300+
246301
public List<P2<K, V>> toList() {
247302
return keys().map(new F<K, P2<K, V>>() {
248303
public P2<K, V> f(final K k) {
@@ -272,6 +327,14 @@ public Array<P2<K, V>> toArray() {
272327
return toList().toArray();
273328
}
274329

330+
public java.util.Map<K, V> toMap() {
331+
final java.util.HashMap<K,V> result = new java.util.HashMap<K, V>();
332+
for (K key : keys()) {
333+
result.put(key, get(key).some());
334+
}
335+
return result;
336+
}
337+
275338
public static <K, V> HashMap<K, V> from(Iterable<P2<K, V>> entries) {
276339
return from(entries, Equal.<K>anyEqual(), Hash.<K>anyHash());
277340
}

core/src/test/fj/data/CheckHashMap.scala

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ package data
33

44
import org.scalacheck.Prop._
55
import ArbitraryHashMap._
6-
import Equal.{intEqual, stringEqual, optionEqual}
7-
import Hash.intHash
6+
import Equal._
7+
import Hash._
8+
import Ord._
89
import fj.data.Option._
910
import scala.collection.JavaConversions._
1011
import org.scalacheck.{Arbitrary, Properties}
1112
import data.ArbitraryList._
1213
import org.scalacheck.Arbitrary._
14+
import java.util.Map
1315

1416
object CheckHashMap extends Properties("HashMap") {
1517
implicit val equalInt: Equal[Int] = intEqual comap ((x: Int) => (x: java.lang.Integer))
@@ -69,4 +71,48 @@ object CheckHashMap extends Properties("HashMap") {
6971
.forall((e: (Int, Iterable[P2[Int, String]])) => e._2
7072
.exists((elem: P2[Int, String]) => optionEqual(stringEqual).eq(map.get(e._1), Option.some(elem._2))))
7173
})
72-
}
74+
75+
property("map") = forAll((m: HashMap[Int, String]) => {
76+
val keyFunction: F[Int, String] = (i: Int) => i.toString
77+
val valueFunction: (String) => String = (s: String) => s + "a"
78+
val mapped = m.map(keyFunction, valueFunction, stringEqual, stringHash)
79+
val keysAreEqual = m.keys().map(keyFunction).toSet == mapped.keys.toSet
80+
val appliedFunctionsToKeysAndValues: Boolean = m.keys().forall((key: Int) => {
81+
val mappedValue = mapped.get(keyFunction.f(key))
82+
val oldValueMapped = some(valueFunction.f(m.get(key).some()))
83+
Equal.optionEqual(stringEqual).eq(mappedValue, oldValueMapped)
84+
})
85+
86+
keysAreEqual && appliedFunctionsToKeysAndValues
87+
})
88+
89+
property("toMap") = forAll((m: HashMap[Int, String]) => {
90+
val toMap: Map[Int, String] = m.toMap
91+
m.keys().forall((key: Int) => m.get(key).some() == toMap.get(key))
92+
})
93+
94+
property("fromMap") = forAll((m: HashMap[Int, String]) => {
95+
val map = new java.util.HashMap[Int, String]()
96+
m.keys().foreach((key: Int) => {
97+
map.put(key, m.get(key).some())
98+
Unit.unit()
99+
})
100+
val fromMap: HashMap[Int, String] = new HashMap[Int, String](map)
101+
val keysAreEqual = m.keys.toSet == fromMap.keys.toSet
102+
val valuesAreEqual = m.keys().forall((key: Int) =>
103+
optionEqual(stringEqual).eq(m.get(key), fromMap.get(key)))
104+
keysAreEqual && valuesAreEqual
105+
})
106+
107+
property("No null values") = forAll((m: List[Int]) => {
108+
val map = HashMap.hashMap[Int, Int]()
109+
m.foreach(new Effect[Int] {
110+
def e(a: Int) {
111+
map.set(a, null.asInstanceOf[Int])
112+
}
113+
})
114+
m.forall(new F[Int, java.lang.Boolean]() {
115+
def f(a: Int) = map.contains(a) == false
116+
})
117+
})
118+
}

0 commit comments

Comments
 (0)