Skip to content

Commit 75e8f6b

Browse files
committed
Adds select() and reject() to JImmutableSet.
Adds transform() and transformIfPresent() to JImmutableListMap and JImmutableSetMap.
1 parent 6a2f903 commit 75e8f6b

File tree

8 files changed

+190
-25
lines changed

8 files changed

+190
-25
lines changed

src/main/java/org/javimmutable/collections/JImmutableListMap.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,45 @@ default JImmutableListMap<K, V> deleteAll(@Nonnull Iterator<? extends K> keys)
167167
return map;
168168
}
169169

170+
/**
171+
* Apply the specified transform function to the List assigned to the specified key and assign the result
172+
* to the key in this map. If no List is currently assigned to the key the transform function is called
173+
* with an empty list.
174+
*
175+
* @param key key holding list to be updated
176+
* @param transform function to update the list
177+
* @return new map with update applied to list associated with key
178+
*/
179+
default JImmutableListMap<K, V> transform(@Nonnull K key,
180+
@Nonnull Func1<JImmutableList<V>, JImmutableList<V>> transform)
181+
{
182+
final JImmutableList<V> current = getList(key);
183+
final JImmutableList<V> transformed = transform.apply(current);
184+
return (transformed == current) ? this : assign(key, transformed);
185+
}
186+
187+
/**
188+
* Apply the specified transform function to the List assigned to the specified key and assign the result
189+
* to the key in this map. If no list is currently assigned to the key the transform function is never
190+
* called and this map is returned unchanged.
191+
*
192+
* @param key key holding list to be updated
193+
* @param transform function to update the list
194+
* @return new map with update applied to list associated with key
195+
*/
196+
default JImmutableListMap<K, V> transformIfPresent(@Nonnull K key,
197+
@Nonnull Func1<JImmutableList<V>, JImmutableList<V>> transform)
198+
{
199+
final JImmutableList<V> current = get(key);
200+
if (current != null) {
201+
final JImmutableList<V> transformed = transform.apply(current);
202+
if (transformed != current) {
203+
return assign(key, transformed);
204+
}
205+
}
206+
return this;
207+
}
208+
170209
/**
171210
* Return the number of keys in the map.
172211
*/

src/main/java/org/javimmutable/collections/JImmutableSet.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import javax.annotation.concurrent.Immutable;
4141
import java.util.Iterator;
4242
import java.util.Set;
43+
import java.util.function.Predicate;
4344

4445
/**
4546
* Interface for immutable sets.
@@ -259,4 +260,44 @@ public interface JImmutableSet<T>
259260
*/
260261
@Nonnull
261262
Set<T> getSet();
263+
264+
/**
265+
* Returns a set of the same type as this containing only those elements for which
266+
* predicate returns true. Implementations are optimized assuming predicate will
267+
* return false more often than true.
268+
*
269+
* @param predicate decides whether to include an element
270+
* @return set of same type as this containing only those elements for which predicate returns true
271+
*/
272+
@Nonnull
273+
default JImmutableSet<T> select(@Nonnull Predicate<T> predicate)
274+
{
275+
JImmutableSet<T> answer = deleteAll();
276+
for (T value : this) {
277+
if (predicate.test(value)) {
278+
answer = answer.insert(value);
279+
}
280+
}
281+
return answer.size() == size() ? this : answer;
282+
}
283+
284+
/**
285+
* Returns a set of the same type as this containing all those elements for which
286+
* predicate returns false. Implementations are optimized assuming predicate will
287+
* return false more often than true.
288+
*
289+
* @param predicate decides whether to include an element
290+
* @return set of same type as this containing only those elements for which predicate returns false
291+
*/
292+
@Nonnull
293+
default JImmutableSet<T> reject(@Nonnull Predicate<T> predicate)
294+
{
295+
JImmutableSet<T> answer = this;
296+
for (T value : this) {
297+
if (predicate.test(value)) {
298+
answer = answer.delete(value);
299+
}
300+
}
301+
return answer.size() == size() ? this : answer;
302+
}
262303
}

src/main/java/org/javimmutable/collections/JImmutableSetMap.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,44 @@ JImmutableSetMap<K, V> intersection(@Nonnull K key,
272272
JImmutableSetMap<K, V> intersection(@Nonnull K key,
273273
@Nonnull Set<? extends V> other);
274274

275+
/**
276+
* Apply the specified transform function to the Set assigned to the specified key and assign the result
277+
* to the key in this map. If no Set is currently assigned to the key the transform function is called
278+
* with an empty set.
279+
*
280+
* @param key key holding set to be updated
281+
* @param transform function to update the set
282+
* @return new map with update applied to set associated with key
283+
*/
284+
default JImmutableSetMap<K, V> transform(@Nonnull K key,
285+
@Nonnull Func1<JImmutableSet<V>, JImmutableSet<V>> transform)
286+
{
287+
final JImmutableSet<V> current = getSet(key);
288+
final JImmutableSet<V> transformed = transform.apply(current);
289+
return (transformed == current) ? this : assign(key, transformed);
290+
}
291+
292+
/**
293+
* Apply the specified transform function to the Set assigned to the specified key and assign the result
294+
* to the key in this map. If no set is currently assigned to the key the transform function is never
295+
* called and this map is returned unchanged.
296+
*
297+
* @param key key holding set to be updated
298+
* @param transform function to update the set
299+
* @return new map with update applied to set associated with key
300+
*/
301+
default JImmutableSetMap<K, V> transformIfPresent(@Nonnull K key,
302+
@Nonnull Func1<JImmutableSet<V>, JImmutableSet<V>> transform)
303+
{
304+
final JImmutableSet<V> current = get(key);
305+
if (current != null) {
306+
final JImmutableSet<V> transformed = transform.apply(current);
307+
if (transformed != current) {
308+
return assign(key, transformed);
309+
}
310+
}
311+
return this;
312+
}
275313

276314
/**
277315
* Return the number of keys in the map.

src/test/java/org/javimmutable/collections/listmap/AbstractJImmutableListMapTestTestCase.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
package org.javimmutable.collections.listmap;
3737

3838
import junit.framework.TestCase;
39+
import org.javimmutable.collections.Func1;
3940
import org.javimmutable.collections.JImmutableList;
4041
import org.javimmutable.collections.JImmutableListMap;
4142
import org.javimmutable.collections.MapEntry;
@@ -46,7 +47,7 @@
4647
import java.util.Collections;
4748

4849
public abstract class AbstractJImmutableListMapTestTestCase
49-
extends TestCase
50+
extends TestCase
5051
{
5152
public JImmutableListMap<Integer, Integer> verifyOperations(JImmutableListMap<Integer, Integer> map)
5253
{
@@ -68,9 +69,9 @@ public JImmutableListMap<Integer, Integer> verifyOperations(JImmutableListMap<In
6869
assertSame(map.getList(1), map.get(1));
6970
assertEquals(2, map.getList(1).size());
7071

71-
map = (JImmutableListMap<Integer, Integer>)map.insert(MapEntry.of(3, 87));
72-
map = (JImmutableListMap<Integer, Integer>)map.insert(MapEntry.of(2, 87));
73-
map = (JImmutableListMap<Integer, Integer>)map.insert(MapEntry.of(1, 87));
72+
map = map.insert(MapEntry.of(3, 87));
73+
map = map.insert(MapEntry.of(2, 87));
74+
map = map.insert(MapEntry.of(1, 87));
7475
assertFalse(map.isEmpty());
7576
assertEquals(3, map.size());
7677
assertEquals(Arrays.asList(100, 18, 87), map.getList(1).getList());
@@ -103,7 +104,33 @@ public JImmutableListMap<Integer, Integer> verifyOperations(JImmutableListMap<In
103104
StandardCursorTest.listCursorTest(Arrays.asList(100, 18, 87), map.valuesCursor(1));
104105
StandardCursorTest.listCursorTest(Arrays.asList(87), map.valuesCursor(2));
105106
StandardCursorTest.listCursorTest(Arrays.asList(300, 7, 7, 14), map.valuesCursor(3));
106-
StandardCursorTest.listCursorTest(Collections.<Integer>emptyList(), map.valuesCursor(4));
107+
StandardCursorTest.listCursorTest(Collections.emptyList(), map.valuesCursor(4));
108+
109+
verifyTransform(map);
110+
107111
return map;
108112
}
113+
114+
private void verifyTransform(JImmutableListMap<Integer, Integer> map)
115+
{
116+
final Func1<JImmutableList<Integer>, JImmutableList<Integer>> removeAll = list -> list.deleteAll();
117+
final Func1<JImmutableList<Integer>, JImmutableList<Integer>> removeLarge = list -> list.reject(x -> x >= 10);
118+
final Func1<JImmutableList<Integer>, JImmutableList<Integer>> removeEven = list -> list.reject(x -> x % 2 == 0);
119+
120+
final int goodKey = 1;
121+
final int badKey = 2;
122+
123+
final JImmutableListMap<Integer, Integer> start = map.deleteAll().insertAll(goodKey, Arrays.asList(1, 2, 3, 4, 5, 6));
124+
final JImmutableList<Integer> oddOnly = start.getList(goodKey).reject(x -> x % 2 == 0);
125+
126+
assertSame(start, start.transform(goodKey, removeLarge));
127+
assertEquals(start.assign(goodKey, oddOnly), start.transform(goodKey, removeEven));
128+
assertSame(start, start.transform(badKey, removeLarge));
129+
130+
assertSame(start, start.transformIfPresent(goodKey, removeLarge));
131+
assertEquals(start.assign(goodKey, oddOnly), start.transformIfPresent(goodKey, removeEven));
132+
assertSame(start, start.transform(badKey, removeLarge));
133+
assertSame(start, start.transformIfPresent(badKey, removeAll));
134+
assertSame(start, start.transformIfPresent(badKey, removeAll));
135+
}
109136
}
Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
package org.javimmutable.collections.setmap;
3737

3838
import junit.framework.TestCase;
39+
import org.javimmutable.collections.Func1;
3940
import org.javimmutable.collections.JImmutableList;
4041
import org.javimmutable.collections.JImmutableMap;
4142
import org.javimmutable.collections.JImmutableSet;
@@ -57,7 +58,7 @@
5758
import java.util.Random;
5859
import java.util.Set;
5960

60-
public abstract class AbstractJImmutableSetMapTestTestCase
61+
public abstract class AbstractJImmutableSetMapTestCase
6162
extends TestCase
6263
{
6364
public JImmutableSetMap<Integer, Integer> verifyOperations(JImmutableSetMap<Integer, Integer> map)
@@ -121,9 +122,36 @@ public JImmutableSetMap<Integer, Integer> verifyOperations(JImmutableSetMap<Inte
121122
StandardCursorTest.listCursorTest(Arrays.asList(87), map.valuesCursor(2));
122123
StandardCursorTest.listCursorTest(Arrays.asList(7, 14, 300), map.valuesCursor(3));
123124
StandardCursorTest.listCursorTest(Collections.emptyList(), map.valuesCursor(4));
125+
126+
verifyTransform(map);
127+
124128
return map;
125129
}
126130

131+
132+
private void verifyTransform(JImmutableSetMap<Integer, Integer> map)
133+
{
134+
final Func1<JImmutableSet<Integer>, JImmutableSet<Integer>> removeAll = set -> set.deleteAll();
135+
final Func1<JImmutableSet<Integer>, JImmutableSet<Integer>> removeLarge = set -> set.reject(x -> x >= 10);
136+
final Func1<JImmutableSet<Integer>, JImmutableSet<Integer>> removeEven = set -> set.reject(x -> x % 2 == 0);
137+
138+
final int goodKey = 1;
139+
final int badKey = 2;
140+
141+
final JImmutableSetMap<Integer, Integer> start = map.deleteAll().insertAll(goodKey, Arrays.asList(1, 2, 3, 4, 5, 6));
142+
final JImmutableSet<Integer> oddOnly = start.getSet(goodKey).reject(x -> x % 2 == 0);
143+
144+
assertSame(start, start.transform(goodKey, removeLarge));
145+
assertEquals(start.assign(goodKey, oddOnly), start.transform(goodKey, removeEven));
146+
assertSame(start, start.transform(badKey, removeLarge));
147+
148+
assertSame(start, start.transformIfPresent(goodKey, removeLarge));
149+
assertEquals(start.assign(goodKey, oddOnly), start.transformIfPresent(goodKey, removeEven));
150+
assertSame(start, start.transform(badKey, removeLarge));
151+
assertSame(start, start.transformIfPresent(badKey, removeAll));
152+
assertSame(start, start.transformIfPresent(badKey, removeAll));
153+
}
154+
127155
private void verifyContains(JImmutableSetMap<Integer, Integer> emptyMap)
128156
{
129157
JImmutableList<Integer> values = JImmutables.list();

src/test/java/org/javimmutable/collections/setmap/JImmutableHashSetMapTest.java

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,34 +36,29 @@
3636
package org.javimmutable.collections.setmap;
3737

3838

39-
import org.javimmutable.collections.JImmutableSet;
40-
import org.javimmutable.collections.JImmutableMap;
4139
import org.javimmutable.collections.JImmutableSetMap;
4240
import org.javimmutable.collections.MapEntry;
4341
import org.javimmutable.collections.cursors.StandardCursorTest;
4442

4543
import java.util.Arrays;
4644
import java.util.HashMap;
47-
import java.util.HashSet;
48-
import java.util.Random;
49-
import java.util.Set;
5045

5146
public class JImmutableHashSetMapTest
52-
extends AbstractJImmutableSetMapTestTestCase
47+
extends AbstractJImmutableSetMapTestCase
5348
{
5449
@SuppressWarnings("unchecked")
5550
public void test()
5651
{
57-
JImmutableSetMap<Integer, Integer> map = verifyOperations(JImmutableHashSetMap.<Integer, Integer>of());
58-
verifyRandom(JImmutableHashSetMap.<Integer, Integer>of(), new HashMap<Integer, Set<Integer>>());
52+
JImmutableSetMap<Integer, Integer> map = verifyOperations(JImmutableHashSetMap.of());
53+
verifyRandom(JImmutableHashSetMap.of(), new HashMap<>());
5954
StandardCursorTest.listCursorTest(Arrays.asList(1, 2, 3), map.keysCursor());
60-
StandardCursorTest.listCursorTest(Arrays.<JImmutableMap.Entry<Integer, JImmutableSet<Integer>>>asList(MapEntry.of(1, map.getSet(1)),
61-
MapEntry.of(2, map.getSet(2)),
62-
MapEntry.of(3, map.getSet(3))),
55+
StandardCursorTest.listCursorTest(Arrays.asList(MapEntry.of(1, map.getSet(1)),
56+
MapEntry.of(2, map.getSet(2)),
57+
MapEntry.of(3, map.getSet(3))),
6358
map.cursor());
64-
StandardCursorTest.listIteratorTest(Arrays.<JImmutableMap.Entry<Integer, JImmutableSet<Integer>>>asList(MapEntry.of(1, map.getSet(1)),
65-
MapEntry.of(2, map.getSet(2)),
66-
MapEntry.of(3, map.getSet(3))),
59+
StandardCursorTest.listIteratorTest(Arrays.asList(MapEntry.of(1, map.getSet(1)),
60+
MapEntry.of(2, map.getSet(2)),
61+
MapEntry.of(3, map.getSet(3))),
6762
map.iterator());
6863
}
6964

src/test/java/org/javimmutable/collections/setmap/JImmutableInsertOrderSetMapTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
import java.util.Set;
4747

4848
public class JImmutableInsertOrderSetMapTest
49-
extends AbstractJImmutableSetMapTestTestCase
49+
extends AbstractJImmutableSetMapTestCase
5050
{
5151
@SuppressWarnings("unchecked")
5252
public void test()

src/test/java/org/javimmutable/collections/setmap/JImmutableTreeSetMapTest.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,11 @@
4343

4444
import java.util.Arrays;
4545
import java.util.Comparator;
46-
import java.util.HashMap;
47-
import java.util.HashSet;
48-
import java.util.Random;
4946
import java.util.Set;
5047
import java.util.TreeMap;
5148

5249
public class JImmutableTreeSetMapTest
53-
extends AbstractJImmutableSetMapTestTestCase
50+
extends AbstractJImmutableSetMapTestCase
5451
{
5552
@SuppressWarnings("unchecked")
5653
public void testNormalOrder()

0 commit comments

Comments
 (0)