diff options
Diffstat (limited to 'src/main/java/bjc/funcdata')
| -rw-r--r-- | src/main/java/bjc/funcdata/ExtendedMap.java | 157 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/Freezable.java | 73 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/FunctionalList.java | 62 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/FunctionalMap.java | 149 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/FunctionalStringTokenizer.java | 6 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/ListEx.java (renamed from src/main/java/bjc/funcdata/IList.java) | 22 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/MapEx.java (renamed from src/main/java/bjc/funcdata/IMap.java) | 146 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/ObjectFrozen.java | 47 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/TransformedValueMap.java | 87 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/bst/BinarySearchTree.java | 21 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java | 2 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java | 8 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/bst/TreePart.java (renamed from src/main/java/bjc/funcdata/bst/ITreePart.java) | 2 |
13 files changed, 468 insertions, 314 deletions
diff --git a/src/main/java/bjc/funcdata/ExtendedMap.java b/src/main/java/bjc/funcdata/ExtendedMap.java index bd500f4..379ff65 100644 --- a/src/main/java/bjc/funcdata/ExtendedMap.java +++ b/src/main/java/bjc/funcdata/ExtendedMap.java @@ -1,8 +1,7 @@ package bjc.funcdata; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; +import java.util.*; +import java.util.function.*; /** * An extended version of a map, that stores values into a map, but can look @@ -16,12 +15,15 @@ import java.util.function.Function; * @param <ValueType> * The type of the values of the map. */ -class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> { +class ExtendedMap<KeyType, ValueType> implements MapEx<KeyType, ValueType> { /* The map we delegate lookups to. */ - private final IMap<KeyType, ValueType> delegate; + private final MapEx<KeyType, ValueType> delegate; /* The map we store things in. */ - private final IMap<KeyType, ValueType> store; + private final MapEx<KeyType, ValueType> store; + private boolean isFrozen = false; + private boolean thawEnabled = true; + /** * Create a new extended map. * @@ -31,28 +33,23 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> { * @param store * The map to store things in. */ - public ExtendedMap(final IMap<KeyType, ValueType> delegate, - final IMap<KeyType, ValueType> store) { + public ExtendedMap(final MapEx<KeyType, ValueType> delegate, + final MapEx<KeyType, ValueType> store) { this.delegate = delegate; this.store = store; } @Override public void clear() { + if (isFrozen) return; + store.clear(); } @Override public boolean containsKey(final KeyType key) { - if (store.containsKey(key)) - return true; - - return delegate.containsKey(key); - } - - @Override - public IMap<KeyType, ValueType> extend() { - return new ExtendedMap<>(this, new FunctionalMap<>()); + if (store.containsKey(key)) return true; + else return delegate.containsKey(key); } @Override @@ -63,25 +60,9 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> { } @Override - public void forEachKey(final Consumer<KeyType> action) { - store.forEachKey(action); - - delegate.forEachKey(action); - } - - @Override - public void forEachValue(final Consumer<ValueType> action) { - store.forEachValue(action); - - delegate.forEachValue(action); - } - - @Override - public ValueType get(final KeyType key) { - if (store.containsKey(key)) - return store.get(key); - - return delegate.get(key); + public Optional<ValueType> get(final KeyType key) { + if (store.containsKey(key)) return store.get(key); + else return delegate.get(key); } @Override @@ -90,8 +71,8 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> { } @Override - public IList<KeyType> keyList() { - IList<KeyType> ilst = new FunctionalList<>(); + public ListEx<KeyType> keyList() { + ListEx<KeyType> ilst = new FunctionalList<>(); ilst.addAll(store.keyList()); ilst.addAll(delegate.keyList()); @@ -100,70 +81,84 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> { } @Override - public <MappedValue> IMap<KeyType, MappedValue> - transform(final Function<ValueType, MappedValue> transformer) { - return new TransformedValueMap<>(this, transformer); - } - - @Override public ValueType put(final KeyType key, final ValueType val) { + if (isFrozen) + throw new ObjectFrozen("Can't insert key " + key + " into frozen map"); + return store.put(key, val); } @Override public ValueType remove(final KeyType key) { - if (!store.containsKey(key)) - return delegate.remove(key); - - return store.remove(key); + if (isFrozen) + throw new ObjectFrozen("Can't remove key " + key + " from frozen map"); + + if (!store.containsKey(key)) return delegate.remove(key); + else return store.remove(key); } @Override - public IList<ValueType> valueList() { - IList<ValueType> ilst = new FunctionalList<>(); + public int hashCode() { + // isFrozen isn't counted + return Objects.hash(delegate, store); + } - ilst.addAll(store.valueList()); - ilst.addAll(delegate.valueList()); + /* Misc. object support. */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; - return ilst; - } + ExtendedMap<?, ?> other = (ExtendedMap<?, ?>) obj; - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (delegate == null ? 0 : delegate.hashCode()); - result = prime * result + (store == null ? 0 : store.hashCode()); - return result; + // isFrozen isn't counted + return Objects.equals(delegate, other.delegate) && Objects.equals(store, other.store); + } + + @Override + public String toString() { + return String.format("ExtendedMap [delegate=%s, store=%s]", delegate, store); } + /* IFreezable support */ + @Override - public boolean equals(final Object obj) { - if (this == obj) + public boolean freeze() { + isFrozen = true; + return true; - if (obj == null) - return false; - if (!(obj instanceof ExtendedMap)) - return false; + } - final ExtendedMap<?, ?> other = (ExtendedMap<?, ?>) obj; + @Override + public boolean deepFreeze() { + thawEnabled = false; - if (delegate == null) { - if (other.delegate != null) - return false; - } else if (!delegate.equals(other.delegate)) - return false; - if (store == null) { - if (other.store != null) - return false; - } else if (!store.equals(other.store)) + return freeze(); + } + + @Override + public boolean thaw() { + if (thawEnabled) { + isFrozen = false; + return true; + } else { return false; + } + } + @Override + public boolean isFrozen() { + return isFrozen; + } + + @Override + public boolean canFreeze() { return true; } - + @Override - public String toString() { - return String.format("ExtendedMap [delegate=%s, store=%s]", delegate, store); + public boolean canThaw() { + return thawEnabled; } -} +}
\ No newline at end of file diff --git a/src/main/java/bjc/funcdata/Freezable.java b/src/main/java/bjc/funcdata/Freezable.java new file mode 100644 index 0000000..e83accb --- /dev/null +++ b/src/main/java/bjc/funcdata/Freezable.java @@ -0,0 +1,73 @@ +package bjc.funcdata; + +/** + * Indicates that an object can switch between immutable and mutable modes. + * + * Note that this only implements 'shallow' immutability. Namely, any sub-objects + * are not made immutable, and if the type is a collection, the elements are still + * as mutable as they were before. + * + * Implementations of this interface may choose to throw {@link ObjectFrozen} if + * you attempt to modify a frozen object, but they may also choose not to. + * + * @author Ben Culkin + */ +public interface Freezable { + /** + * Freezes the internal state of this object, making it immutable. + * + * @return True if the object is frozen, false if it couldn't be frozen. + */ + public boolean freeze(); + /** + * Thaws the internal state of this object, making it mutable. + * + * @return True if the object is thawed, false if it couldn't be thawed. + */ + public boolean thaw(); + + /** + * 'Deep-freeze' this object, making it immutable and disabling the ability to + * thaw it. + * + * @return True if the object was deep-frozen, false if that couldn't happen. + */ + default boolean deepFreeze() { + return false; + } + + /** + * Check if this object can be frozen. + * + * @return Whether or not the object can be frozen. + */ + default boolean canFreeze() { + return false; + } + + /** + * Checks if this object can be thawed. + * + * @return Whether or not the object can be thawed. + */ + default boolean canThaw() { + return false; + } + + /** + * Determines if this object is frozen. + * + * @return True if the object is frozen, false if the object is thawed. + */ + public boolean isFrozen(); + + + /** + * Determines if this object is thawed. + * + * @return True if the object is thawed, false if the object is thawed. + */ + default boolean isThawed() { + return !isFrozen(); + } +} diff --git a/src/main/java/bjc/funcdata/FunctionalList.java b/src/main/java/bjc/funcdata/FunctionalList.java index 2cdfa27..88f49c4 100644 --- a/src/main/java/bjc/funcdata/FunctionalList.java +++ b/src/main/java/bjc/funcdata/FunctionalList.java @@ -12,10 +12,10 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; -import bjc.data.IHolder; -import bjc.data.IPair; -import bjc.data.Identity; +import bjc.data.Holder; import bjc.data.Pair; +import bjc.data.Identity; +import bjc.data.SimplePair; /** * A wrapper over another list that provides eager functional operations over @@ -29,7 +29,7 @@ import bjc.data.Pair; * @param <E> * The type in this list */ -public class FunctionalList<E> implements Cloneable, IList<E> { +public class FunctionalList<E> implements Cloneable, ListEx<E> { /* The list used as a backing store */ private final List<E> wrapped; @@ -60,20 +60,21 @@ public class FunctionalList<E> implements Cloneable, IList<E> { * * Takes O(n) time, where n is the number of items specified * - * @param items - * The items to put into this functional list. + * @param <T> The type of items to put into the list. + * + * @param items The items to put into this functional list. + * * @return The returned list. */ @SafeVarargs - public static <T> IList<T> listOf(final T... items) { + public static <T> ListEx<T> listOf(final T... items) { return new FunctionalList<>(items); } /** * Create a new functional list with the specified size. * - * @param size - * The size of the backing list . + * @param size The size of the backing list . */ private FunctionalList(final int size) { wrapped = new ArrayList<>(size); @@ -84,8 +85,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> { * * Takes O(1) time, since it doesn't copy the list. * - * @param backing - * The list to use as a backing list. + * @param backing The list to use as a backing list. */ public FunctionalList(final List<E> backing) { if (backing == null) @@ -137,8 +137,8 @@ public class FunctionalList<E> implements Cloneable, IList<E> { * @return A copy of the list. */ @Override - public IList<E> clone() { - final IList<E> cloned = new FunctionalList<>(); + public ListEx<E> clone() { + final ListEx<E> cloned = new FunctionalList<>(); for (final E element : wrapped) { cloned.add(element); @@ -148,7 +148,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> { } @Override - public <T, F> IList<F> combineWith(final IList<T> rightList, + public <T, F> ListEx<F> combineWith(final ListEx<T> rightList, final BiFunction<E, T, F> itemCombiner) { if (rightList == null) { throw new NullPointerException("Target combine list must not be null"); @@ -156,7 +156,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> { throw new NullPointerException("Combiner must not be null"); } - final IList<F> returned = new FunctionalList<>(); + final ListEx<F> returned = new FunctionalList<>(); /* Get the iterator for the other list. */ final Iterator<T> rightIterator = rightList.toIterable().iterator(); @@ -222,14 +222,14 @@ public class FunctionalList<E> implements Cloneable, IList<E> { } @Override - public <T> IList<T> flatMap(final Function<E, IList<T>> expander) { + public <T> ListEx<T> flatMap(final Function<E, ListEx<T>> expander) { if (expander == null) throw new NullPointerException("Expander must not be null"); - final IList<T> returned = new FunctionalList<>(this.wrapped.size()); + final ListEx<T> returned = new FunctionalList<>(this.wrapped.size()); forEach(element -> { - final IList<T> expandedElement = expander.apply(element); + final ListEx<T> expandedElement = expander.apply(element); if (expandedElement == null) throw new NullPointerException("Expander returned null list"); @@ -257,7 +257,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> { /* * This is held b/c ref'd variables must be final/effectively final. */ - final IHolder<Integer> currentIndex = new Identity<>(0); + final Holder<Integer> currentIndex = new Identity<>(0); wrapped.forEach(element -> { /* Call the action with the index and the value. */ @@ -283,11 +283,11 @@ public class FunctionalList<E> implements Cloneable, IList<E> { } @Override - public IList<E> getMatching(final Predicate<E> predicate) { + public ListEx<E> getMatching(final Predicate<E> predicate) { if (predicate == null) throw new NullPointerException("Predicate must not be null"); - final IList<E> returned = new FunctionalList<>(); + final ListEx<E> returned = new FunctionalList<>(); wrapped.forEach(element -> { if (predicate.test(element)) { @@ -313,17 +313,17 @@ public class FunctionalList<E> implements Cloneable, IList<E> { /* Check if a partition has room for another item. */ private Boolean isPartitionFull(final int numberPerPartition, - final IHolder<IList<E>> currentPartition) { + final Holder<ListEx<E>> currentPartition) { return currentPartition .unwrap(partition -> partition.getSize() >= numberPerPartition); } @Override - public <T> IList<T> map(final Function<E, T> elementTransformer) { + public <T> ListEx<T> map(final Function<E, T> elementTransformer) { if (elementTransformer == null) throw new NullPointerException("Transformer must be not null"); - final IList<T> returned = new FunctionalList<>(this.wrapped.size()); + final ListEx<T> returned = new FunctionalList<>(this.wrapped.size()); forEach(element -> { // Add the transformed item to the result @@ -334,12 +334,12 @@ public class FunctionalList<E> implements Cloneable, IList<E> { } @Override - public <T> IList<IPair<E, T>> pairWith(final IList<T> rightList) { - return combineWith(rightList, Pair<E, T>::new); + public <T> ListEx<Pair<E, T>> pairWith(final ListEx<T> rightList) { + return combineWith(rightList, SimplePair<E, T>::new); } @Override - public IList<IList<E>> partition(final int numberPerPartition) { + public ListEx<ListEx<E>> partition(final int numberPerPartition) { if (numberPerPartition < 1 || numberPerPartition > wrapped.size()) { final String fmt = "%s is an invalid partition size. Must be between 1 and %d"; @@ -348,10 +348,10 @@ public class FunctionalList<E> implements Cloneable, IList<E> { throw new IllegalArgumentException(msg); } - final IList<IList<E>> returned = new FunctionalList<>(); + final ListEx<ListEx<E>> returned = new FunctionalList<>(); /* The current partition being filled. */ - final IHolder<IList<E>> currentPartition = new Identity<>(new FunctionalList<>()); + final Holder<ListEx<E>> currentPartition = new Identity<>(new FunctionalList<>()); this.forEach(element -> { if (isPartitionFull(numberPerPartition, currentPartition)) { @@ -395,7 +395,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> { } /* The current collapsed list. */ - final IHolder<T> currentState = new Identity<>(initialValue); + final Holder<T> currentState = new Identity<>(initialValue); wrapped.forEach(element -> { /* Accumulate a new value into the state. */ @@ -444,7 +444,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> { } @Override - public IList<E> tail() { + public ListEx<E> tail() { return new FunctionalList<>(wrapped.subList(1, getSize())); } diff --git a/src/main/java/bjc/funcdata/FunctionalMap.java b/src/main/java/bjc/funcdata/FunctionalMap.java index aba3dd1..3427a91 100644 --- a/src/main/java/bjc/funcdata/FunctionalMap.java +++ b/src/main/java/bjc/funcdata/FunctionalMap.java @@ -1,15 +1,12 @@ package bjc.funcdata; -import java.util.HashMap; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; +import java.util.*; +import java.util.function.*; -import bjc.data.IPair; +import bjc.data.*; /** - * Basic implementation of {@link IMap} + * Basic implementation of {@link MapEx} * * @author ben * @@ -19,10 +16,13 @@ import bjc.data.IPair; * @param <ValueType> * The type of the map's values. */ -public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueType> { +public class FunctionalMap<KeyType, ValueType> implements MapEx<KeyType, ValueType> { /* Our backing store. */ private Map<KeyType, ValueType> wrappedMap; + private boolean isFrozen = false; + private boolean thawEnabled = true; + /** Create a new blank functional map */ public FunctionalMap() { wrappedMap = new HashMap<>(); @@ -35,13 +35,11 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp * The entries to put into the map. */ @SafeVarargs - public FunctionalMap(final IPair<KeyType, ValueType>... entries) { + public FunctionalMap(final Pair<KeyType, ValueType>... entries) { this(); - for (final IPair<KeyType, ValueType> entry : entries) { - entry.doWith((key, val) -> { - wrappedMap.put(key, val); - }); + for (final Pair<KeyType, ValueType> entry : entries) { + entry.doWith(wrappedMap::put); } } @@ -52,14 +50,15 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp * The map to wrap. */ public FunctionalMap(final Map<KeyType, ValueType> wrap) { - if (wrap == null) - throw new NullPointerException("Map to wrap must not be null"); + if (wrap == null) throw new NullPointerException("Map to wrap must not be null"); wrappedMap = wrap; } @Override public void clear() { + if (isFrozen) throw new ObjectFrozen("Can't clear frozen map"); + wrappedMap.clear(); } @@ -69,37 +68,19 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp } @Override - public IMap<KeyType, ValueType> extend() { - return new ExtendedMap<>(this, new FunctionalMap<>()); - } - - @Override public void forEach(final BiConsumer<KeyType, ValueType> action) { wrappedMap.forEach(action); } @Override - public void forEachKey(final Consumer<KeyType> action) { - wrappedMap.keySet().forEach(action); - } + public Optional<ValueType> get(final KeyType key) { + if (key == null) throw new NullPointerException("Key must not be null"); - @Override - public void forEachValue(final Consumer<ValueType> action) { - wrappedMap.values().forEach(action); - } - - @Override - public ValueType get(final KeyType key) { - if (key == null) - throw new NullPointerException("Key must not be null"); - - if (!wrappedMap.containsKey(key)) { - final String msg = String.format("Key %s is not present in the map", key); - - throw new IllegalArgumentException(msg); + if (wrappedMap.containsKey(key)) { + return Optional.of(wrappedMap.get(key)); + } else { + return Optional.empty(); } - - return wrappedMap.get(key); } @Override @@ -108,35 +89,26 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp } @Override - public IList<KeyType> keyList() { + public ListEx<KeyType> keyList() { final FunctionalList<KeyType> keys = new FunctionalList<>(); - wrappedMap.keySet().forEach(key -> { - keys.add(key); - }); + wrappedMap.keySet().forEach(keys::add); return keys; } @Override - public <MappedValue> IMap<KeyType, MappedValue> - transform(final Function<ValueType, MappedValue> transformer) { - if (transformer == null) - throw new NullPointerException("Transformer must not be null"); - - return new TransformedValueMap<>(this, transformer); - } - - @Override public ValueType put(final KeyType key, final ValueType val) { - if (key == null) - throw new NullPointerException("Key must not be null"); + if (isFrozen) throw new ObjectFrozen("Can't put key " + key + " into frozen map"); + if (key == null) throw new NullPointerException("Key must not be null"); return wrappedMap.put(key, val); } @Override public ValueType remove(final KeyType key) { + if (isFrozen) throw new ObjectFrozen("Can't remove key " + key + " from frozen map"); + return wrappedMap.remove(key); } @@ -144,18 +116,7 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp public String toString() { return wrappedMap.toString(); } - - @Override - public IList<ValueType> valueList() { - final FunctionalList<ValueType> values = new FunctionalList<>(); - - wrappedMap.values().forEach(value -> { - values.add(value); - }); - - return values; - } - + @Override public int hashCode() { final int prime = 31; @@ -166,20 +127,58 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp @Override public boolean equals(final Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof FunctionalMap)) - return false; + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof FunctionalMap)) return false; final FunctionalMap<?, ?> other = (FunctionalMap<?, ?>) obj; if (wrappedMap == null) { - if (other.wrappedMap != null) - return false; - } else if (!wrappedMap.equals(other.wrappedMap)) + if (other.wrappedMap != null) return false; + } else if (!wrappedMap.equals(other.wrappedMap)) { return false; + } + + return true; + } + + // IFreezable support + @Override + public boolean freeze() { + isFrozen = true; + return true; } + + @Override + public boolean thaw() { + if (thawEnabled) { + isFrozen = false; + return true; + } else { + return false; + } + } + + @Override + public boolean deepFreeze() { + thawEnabled = false; + + return freeze(); + } + + @Override + public boolean canFreeze() { + return true; + } + + @Override + public boolean canThaw() { + return thawEnabled; + } + + @Override + public boolean isFrozen() { + return isFrozen; + } } diff --git a/src/main/java/bjc/funcdata/FunctionalStringTokenizer.java b/src/main/java/bjc/funcdata/FunctionalStringTokenizer.java index 856c153..3344e05 100644 --- a/src/main/java/bjc/funcdata/FunctionalStringTokenizer.java +++ b/src/main/java/bjc/funcdata/FunctionalStringTokenizer.java @@ -128,7 +128,7 @@ public class FunctionalStringTokenizer { * * @return This tokenizer, converted into a list of strings. */ - public IList<String> toList() { + public ListEx<String> toList() { return toList((final String element) -> element); } @@ -144,11 +144,11 @@ public class FunctionalStringTokenizer { * * @return A list containing all of the converted tokens. */ - public <E> IList<E> toList(final Function<String, E> transformer) { + public <E> ListEx<E> toList(final Function<String, E> transformer) { if (transformer == null) throw new NullPointerException("Transformer must not be null"); - final IList<E> returned = new FunctionalList<>(); + final ListEx<E> returned = new FunctionalList<>(); /* Add each token to the list after transforming it. */ forEachToken(token -> { diff --git a/src/main/java/bjc/funcdata/IList.java b/src/main/java/bjc/funcdata/ListEx.java index 0ff8e30..164a71d 100644 --- a/src/main/java/bjc/funcdata/IList.java +++ b/src/main/java/bjc/funcdata/ListEx.java @@ -9,7 +9,7 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collector; -import bjc.data.IPair; +import bjc.data.Pair; import bjc.functypes.ID; /** @@ -20,7 +20,7 @@ import bjc.functypes.ID; * @param <ContainedType> * The type in this list */ -public interface IList<ContainedType> extends Iterable<ContainedType> { +public interface ListEx<ContainedType> extends Iterable<ContainedType> { /** * Add an item to this list. * @@ -40,7 +40,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> { * @return True if every item was successfully added to the list, false * otherwise. */ - default boolean addAll(final IList<ContainedType> items) { + default boolean addAll(final ListEx<ContainedType> items) { return items.map(this::add).anyMatch(bl -> bl == false); } @@ -136,7 +136,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> { * * @return A new list containing the merged pairs of lists. */ - <OtherType, CombinedType> IList<CombinedType> combineWith(IList<OtherType> list, + <OtherType, CombinedType> ListEx<CombinedType> combineWith(ListEx<OtherType> list, BiFunction<ContainedType, OtherType, CombinedType> combiner); /** @@ -191,8 +191,8 @@ public interface IList<ContainedType> extends Iterable<ContainedType> { * @return A new list containing the flattened results of applying the provided * function. */ - <MappedType> IList<MappedType> - flatMap(Function<ContainedType, IList<MappedType>> expander); + <MappedType> ListEx<MappedType> + flatMap(Function<ContainedType, ListEx<MappedType>> expander); /** * Apply a given action for each member of the list. @@ -230,7 +230,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> { * * @return A list containing all elements that match the predicate. */ - IList<ContainedType> getMatching(Predicate<ContainedType> predicate); + ListEx<ContainedType> getMatching(Predicate<ContainedType> predicate); /** * Retrieve the size of the wrapped list. @@ -259,7 +259,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> { * * @return A new list containing the mapped elements of this list. */ - <MappedType> IList<MappedType> map(Function<ContainedType, MappedType> transformer); + <MappedType> ListEx<MappedType> map(Function<ContainedType, MappedType> transformer); /** * Zip two lists into a list of pairs. @@ -272,7 +272,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> { * * @return A list containing pairs of this element and the specified list. */ - <OtherType> IList<IPair<ContainedType, OtherType>> pairWith(IList<OtherType> list); + <OtherType> ListEx<Pair<ContainedType, OtherType>> pairWith(ListEx<OtherType> list); /** * Partition this list into a list of sublists. @@ -285,7 +285,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> { * partition may not be completely full if the size of the list is not a * multiple of partitionSize. */ - IList<IList<ContainedType>> partition(int partitionSize); + ListEx<ListEx<ContainedType>> partition(int partitionSize); /** * Prepend an item to the list. @@ -429,7 +429,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> { * * @return The list without the first element. */ - IList<ContainedType> tail(); + ListEx<ContainedType> tail(); /** * Convert this list into an array. diff --git a/src/main/java/bjc/funcdata/IMap.java b/src/main/java/bjc/funcdata/MapEx.java index dc5ee00..a7af4c5 100644 --- a/src/main/java/bjc/funcdata/IMap.java +++ b/src/main/java/bjc/funcdata/MapEx.java @@ -1,8 +1,7 @@ package bjc.funcdata; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; +import java.util.*; +import java.util.function.*; /** * Functional wrapper over map providing some useful things. @@ -15,7 +14,7 @@ import java.util.function.Function; * @param <ValueType> * The type of this map's values. */ -public interface IMap<KeyType, ValueType> { +public interface MapEx<KeyType, ValueType> extends Freezable { /** * Execute an action for each entry in the map. * @@ -62,31 +61,7 @@ public interface IMap<KeyType, ValueType> { * * @return The value of the key. */ - ValueType get(KeyType key); - - /** - * Get a value from the map, and return a default value if the key doesn't - * exist. - * - * @param key - * The key to attempt to retrieve. - * - * @param defaultValue - * The value to return if the key doesn't exist. - * - * @return The value associated with the key, or the default value if the key - * doesn't exist. - */ - default ValueType getOrDefault(final KeyType key, final ValueType defaultValue) { - try { - return get(key); - } catch (final IllegalArgumentException iaex) { - /* - * We don't care about this, because it indicates a key is missing. - */ - return defaultValue; - } - } + Optional<ValueType> get(KeyType key); /** * Add an entry to the map. @@ -109,7 +84,9 @@ public interface IMap<KeyType, ValueType> { /** Delete all the values in the map. */ default void clear() { - keyList().forEach(key -> remove(key)); + if (isFrozen()) throw new ObjectFrozen("Can't clear a frozen map"); + + keyList().forEach(MapEx.this::remove); } /** @@ -121,11 +98,47 @@ public interface IMap<KeyType, ValueType> { return keyList().getSize(); } + /** + * Remove the value bound to the key. + * + * @param key + * The key to remove from the map. + * + * @return The previous value for the key in the map, or null if the key wasn't + * in the class. NOTE: Just because you received null, doesn't mean the + * map wasn't changed. It may mean that someone put a null value for + * that key into the map. + */ + ValueType remove(KeyType key); + + /** + * Get a list of all the keys in this map. + * + * @return A list of all the keys in this map. + */ + ListEx<KeyType> keyList(); + + /** + * Get a list of the values in this map. + * + * @return A list of values in this map. + */ + default ListEx<ValueType> valueList() { + final ListEx<ValueType> returns = new FunctionalList<>(); + + for (final KeyType key : keyList()) { + returns.add(get(key).orElse(null)); + } + + return returns; + } + /* * @NOTE Do we want this to be the semantics for transform, or do we want to go * to semantics using something like Isomorphism, or doing a one-time bulk * conversion of the values? */ + /** * Transform the values returned by this map. * @@ -141,7 +154,7 @@ public interface IMap<KeyType, ValueType> { * * @return The map where each value will be transformed after lookup. */ - default <V2> IMap<KeyType, V2> transform(final Function<ValueType, V2> transformer) { + default <V2> MapEx<KeyType, V2> transform(final Function<ValueType, V2> transformer) { return new TransformedValueMap<>(this, transformer); } @@ -151,40 +164,53 @@ public interface IMap<KeyType, ValueType> { * * @return An extended map. */ - IMap<KeyType, ValueType> extend(); + default MapEx<KeyType, ValueType> extend() { + return extend(new FunctionalMap<>()); + }; + /** - * Remove the value bound to the key. - * - * @param key - * The key to remove from the map. - * - * @return The previous value for the key in the map, or null if the key wasn't - * in the class. NOTE: Just because you received null, doesn't mean the - * map wasn't changed. It may mean that someone put a null value for - * that key into the map. + * Extend this map, creating a new map that will delegate queries to + * the current map but store any added values in the provided map. + * + * @param backer The map to store values in. + * + * @return An extended map, with the specified backing map. */ - ValueType remove(KeyType key); - + default MapEx<KeyType, ValueType> extend(MapEx<KeyType, ValueType> backer) { + return new ExtendedMap<>(this, backer); + }; + /** - * Get a list of all the keys in this map. - * - * @return A list of all the keys in this map. + * Static method to create a basic instance of IMap. + * + * @param <KeyType2> The key type of the map. + * @param <ValueType2> The value type of the map. + * + * @param args A series of key-value pairs. You will get an error if you don't + * provide the correct number of arguments (a number divisible by 2); + * however, if you pass arguments of the wrong type, you will not + * get a compile error, and usually won't get a runtime error in + * construction. However, you will get a ClassCastException as soon + * as you do something that attempts to iterate over the keys or values + * of the map. + * + * @return A map, constructed from the provided values. + * + * @throws IllegalArgumentException If you provide an incomplete pair of arguments. */ - IList<KeyType> keyList(); - - /** - * Get a list of the values in this map. - * - * @return A list of values in this map. - */ - default IList<ValueType> valueList() { - final IList<ValueType> returns = new FunctionalList<>(); - - for (final KeyType key : keyList()) { - returns.add(get(key)); + @SuppressWarnings("unchecked") + static <KeyType2, ValueType2> MapEx<KeyType2, ValueType2> of(Object... args) { + if (args.length % 2 != 0) throw new IllegalArgumentException("Args must be in the form of key-value pairs"); + + MapEx<KeyType2, ValueType2> map = new FunctionalMap<>(); + for (int index = 0; index < args.length; index += 2) { + KeyType2 key = (KeyType2) args[index]; + ValueType2 value = (ValueType2) args[index + 1]; + + map.put(key, value); } - - return returns; + + return map; } } diff --git a/src/main/java/bjc/funcdata/ObjectFrozen.java b/src/main/java/bjc/funcdata/ObjectFrozen.java new file mode 100644 index 0000000..a9f58b8 --- /dev/null +++ b/src/main/java/bjc/funcdata/ObjectFrozen.java @@ -0,0 +1,47 @@ +package bjc.funcdata; + +/** + * Exception that implementations of {@link Freezable} can throw if you attempt + * to modify a frozen object. + * + * @author Ben Culkin + * + */ +public class ObjectFrozen extends RuntimeException { + private static final long serialVersionUID = -1567447627139090728L; + + /** + * Create a new ObjectFrozen exception. + */ + public ObjectFrozen() { + super(); + } + + /** + * Create a new ObjectFrozen exception. + * + * @param message The message of the exception. + */ + public ObjectFrozen(String message) { + super(message); + } + + /** + * Create a new ObjectFrozen exception. + * + * @param cause The root cause of this exception. + */ + public ObjectFrozen(Throwable cause) { + super(cause); + } + + /** + * Create a new ObjectFrozen exception. + * + * @param message The message of the exception. + * @param cause The root cause of this exception. + */ + public ObjectFrozen(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/bjc/funcdata/TransformedValueMap.java b/src/main/java/bjc/funcdata/TransformedValueMap.java index 5de6fc3..643f610 100644 --- a/src/main/java/bjc/funcdata/TransformedValueMap.java +++ b/src/main/java/bjc/funcdata/TransformedValueMap.java @@ -1,8 +1,7 @@ package bjc.funcdata; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; +import java.util.*; +import java.util.function.*; /** * A map that transforms values from one type to another @@ -20,12 +19,15 @@ import java.util.function.Function; * */ final class TransformedValueMap<OldKey, OldValue, NewValue> - implements IMap<OldKey, NewValue> { + implements MapEx<OldKey, NewValue> { /* Our backing map. */ - private final IMap<OldKey, OldValue> backing; + private final MapEx<OldKey, OldValue> backing; /* Our transforming function. */ private final Function<OldValue, NewValue> transformer; + private boolean isFrozen = false; + private boolean thawEnabled = true; + /** * Create a new transformed-value loop. * @@ -35,7 +37,7 @@ final class TransformedValueMap<OldKey, OldValue, NewValue> * @param transform * The function to use for the transform. */ - public TransformedValueMap(final IMap<OldKey, OldValue> backingMap, + public TransformedValueMap(final MapEx<OldKey, OldValue> backingMap, final Function<OldValue, NewValue> transform) { backing = backingMap; transformer = transform; @@ -43,6 +45,8 @@ final class TransformedValueMap<OldKey, OldValue, NewValue> @Override public void clear() { + if (isFrozen) throw new ObjectFrozen("Can't clear frozen map"); + backing.clear(); } @@ -52,11 +56,6 @@ final class TransformedValueMap<OldKey, OldValue, NewValue> } @Override - public IMap<OldKey, NewValue> extend() { - return new ExtendedMap<>(this, new FunctionalMap<>()); - } - - @Override public void forEach(final BiConsumer<OldKey, NewValue> action) { backing.forEach((key, value) -> { action.accept(key, transformer.apply(value)); @@ -64,20 +63,8 @@ final class TransformedValueMap<OldKey, OldValue, NewValue> } @Override - public void forEachKey(final Consumer<OldKey> action) { - backing.forEachKey(action); - } - - @Override - public void forEachValue(final Consumer<NewValue> action) { - backing.forEachValue(value -> { - action.accept(transformer.apply(value)); - }); - } - - @Override - public NewValue get(final OldKey key) { - return transformer.apply(backing.get(key)); + public Optional<NewValue> get(final OldKey key) { + return backing.get(key).map(transformer); } @Override @@ -86,23 +73,19 @@ final class TransformedValueMap<OldKey, OldValue, NewValue> } @Override - public IList<OldKey> keyList() { + public ListEx<OldKey> keyList() { return backing.keyList(); } @Override - public <MappedValue> IMap<OldKey, MappedValue> - transform(final Function<NewValue, MappedValue> transform) { - return new TransformedValueMap<>(this, transform); - } - - @Override public NewValue put(final OldKey key, final NewValue value) { throw new UnsupportedOperationException("Can't add items to transformed map"); } @Override public NewValue remove(final OldKey key) { + if (isFrozen) throw new ObjectFrozen("Can't remove key " + key + " from frozen map"); + return transformer.apply(backing.remove(key)); } @@ -110,9 +93,43 @@ final class TransformedValueMap<OldKey, OldValue, NewValue> public String toString() { return backing.toString(); } + + @Override + public boolean freeze() { + isFrozen = true; + + return true; + } + + @Override + public boolean thaw() { + if (thawEnabled) { + isFrozen = false; + return true; + } else { + return false; + } + } @Override - public IList<NewValue> valueList() { - return backing.valueList().map(transformer); + public boolean deepFreeze() { + thawEnabled = false; + + return freeze(); + } + + @Override + public boolean canFreeze() { + return true; + } + + @Override + public boolean canThaw() { + return thawEnabled; + } + + @Override + public boolean isFrozen() { + return isFrozen; } -} +}
\ No newline at end of file diff --git a/src/main/java/bjc/funcdata/bst/BinarySearchTree.java b/src/main/java/bjc/funcdata/bst/BinarySearchTree.java index e22a8da..99b67cd 100644 --- a/src/main/java/bjc/funcdata/bst/BinarySearchTree.java +++ b/src/main/java/bjc/funcdata/bst/BinarySearchTree.java @@ -6,7 +6,7 @@ import java.util.List; import java.util.function.Predicate; import bjc.funcdata.FunctionalList; -import bjc.funcdata.IList; +import bjc.funcdata.ListEx; /** * A binary search tree, with some mild support for functional traversal. @@ -24,7 +24,7 @@ public class BinarySearchTree<T> { private int elementCount; /* The root element of the tree */ - private ITreePart<T> root; + private TreePart<T> root; /** * Create a new tree using the specified way to compare elements. @@ -33,8 +33,7 @@ public class BinarySearchTree<T> { * The thing to use for comparing elements */ public BinarySearchTree(final Comparator<T> cmp) { - if (cmp == null) - throw new NullPointerException("Comparator must not be null"); + if (cmp == null) throw new NullPointerException("Comparator must not be null"); elementCount = 0; comparator = cmp; @@ -49,11 +48,8 @@ public class BinarySearchTree<T> { public void addNode(final T element) { elementCount++; - if (root == null) { - root = new BinarySearchTreeNode<>(element, null, null); - } else { - root.add(element, comparator); - } + if (root == null) root = new BinarySearchTreeNode<>(element, null, null); + else root.add(element, comparator); } /** @@ -70,7 +66,7 @@ public class BinarySearchTree<T> { * * @return Whether the adjusted pivot is with the list. */ - private boolean adjustedPivotInBounds(final IList<T> elements, final int pivot, + private boolean adjustedPivotInBounds(final ListEx<T> elements, final int pivot, final int pivotAdjustment) { return ((pivot - pivotAdjustment) >= 0) && ((pivot + pivotAdjustment) < elements.getSize()); @@ -82,7 +78,7 @@ public class BinarySearchTree<T> { * Takes O(N) time, but also O(N) space. */ public void balance() { - final IList<T> elements = new FunctionalList<>(); + final ListEx<T> elements = new FunctionalList<>(); /* Add each element to the list in sorted order. */ root.forEach(TreeLinearizationMethod.INORDER, element -> elements.add(element)); @@ -139,7 +135,7 @@ public class BinarySearchTree<T> { * * @return The root of the tree. */ - public ITreePart<T> getRoot() { + public TreePart<T> getRoot() { return root; } @@ -184,6 +180,7 @@ public class BinarySearchTree<T> { */ traverse(TreeLinearizationMethod.PREORDER, node -> { nodes.add(node); + return true; }); diff --git a/src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java b/src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java index 0b99cad..9532555 100644 --- a/src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java +++ b/src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java @@ -13,7 +13,7 @@ import java.util.function.Predicate; * @param <T> * The data stored in the tree. */ -public class BinarySearchTreeLeaf<T> implements ITreePart<T> { +public class BinarySearchTreeLeaf<T> implements TreePart<T> { /** The data held in this tree leaf */ protected T data; diff --git a/src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java b/src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java index a73f81a..0eef92a 100644 --- a/src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java +++ b/src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java @@ -20,10 +20,10 @@ import java.util.function.Predicate; */ public class BinarySearchTreeNode<T> extends BinarySearchTreeLeaf<T> { /* The left child of this node */ - private ITreePart<T> left; + private TreePart<T> left; /* The right child of this node */ - private ITreePart<T> right; + private TreePart<T> right; /** * Create a new node with the specified data and children. @@ -37,8 +37,8 @@ public class BinarySearchTreeNode<T> extends BinarySearchTreeLeaf<T> { * @param rght * The right child of this node. */ - public BinarySearchTreeNode(final T element, final ITreePart<T> lft, - final ITreePart<T> rght) { + public BinarySearchTreeNode(final T element, final TreePart<T> lft, + final TreePart<T> rght) { super(element); this.left = lft; this.right = rght; diff --git a/src/main/java/bjc/funcdata/bst/ITreePart.java b/src/main/java/bjc/funcdata/bst/TreePart.java index bac640d..b451463 100644 --- a/src/main/java/bjc/funcdata/bst/ITreePart.java +++ b/src/main/java/bjc/funcdata/bst/TreePart.java @@ -13,7 +13,7 @@ import java.util.function.Predicate; * @param <T> * The data contained in this part of the tree. */ -public interface ITreePart<T> { +public interface TreePart<T> { /** * Add a element below this tree part somewhere. * |
