org.organicdesign.fp.collections.UnmodCollection Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of UncleJim Show documentation
Show all versions of UncleJim Show documentation
Immutable Clojure collections and a Transformation abstraction for Java 8+, immutably, type-safely, and with good performance. Name will change to "Paguro" in November 2016.
// Copyright 2015 PlanBase Inc. & Glen Peterson
//
// Licensed 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.organicdesign.fp.collections;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.function.Predicate;
/**
Don't implement this interface directly if you don't have to. A collection is an
{@link java.lang.Iterable} with a size (a size() method) and unfortunately a contains() method
(deprecated on Lists).
Collection defines the return of Map.values() which can have duplicates and may be ordered, or
unordered. For this reason, I don't think it's possible to define an equals() method on Collection
that works in all circumstances (when comparing it to a List, a Set, or another amorphous
Collection). I don't think Map.values() would exist if Generics had existed and Map had
implemented Iterable<Entry> from the beginning.
UnmodCollection is an unmodifiable version of {@link java.util.Collection}
which formalizes the return type of Collections.unmodifiableCollection() and Map.values().
*/
public interface UnmodCollection extends Collection, UnmodIterable {
// ========================================== Static ==========================================
// /**
// Don't use this. There may not be any way to implement equals() meaningfully on a Collection
// because the definition of Collection is too broad.
//
// Implements equals and hashCode() methods to make defining unmod sets easier, especially for
// implementing Map.values() and such.
// */
// abstract class AbstractUnmodCollection implements UnmodCollection {
// @SuppressWarnings("unchecked")
// @Override public boolean equals(Object other) {
// if (this == other) { return true; }
// if ( !(other instanceof Collection) ) { return false; }
// Collection that = (Collection) other;
// if (size() != that.size()) { return false; }
//
// // A set may contain all the elements of a list, plus additional elements, and have the
// // same size as a list that contains duplicates. Equality for lists and
// // sets is not the same. Lists are ordered and have duplicates. Sets have no duplicates
// // and may or may not be ordered.
// //
// // The only place that a Collection is returned and needs to live with an equals method
// // is on Map.values(). It can contain duplicates, so it's not a set. It can be ordered
// // (as in SortedMap.values()) which could equal a List, or unordered (just Map.values())
// // which can't be a Set because it needs to contain duplicates. Ugh, that one method
// // is an abomination and should not exist. If only Map had implemented Iterable
// // none of this would have been necessary.
// //
// // In order to have reflexive equals, check first for legitimate child interfaces
// // and let the other object compare itself to this one. I don't like the idea of
// // saying, "Are we equal? I don't know. What do you think?" because another class
// // could do the same thing and go into an infinite call loop (trampoline loop?).
// // So even though this is maybe unordered, we'll compare the random ordering to the
// // list.
// //
// // Hmm... Maybe there is no java.util.AbstractCollection because there is no sensible
// // way to implement it. Ditto why java.util.Map.values() doesn't implement equals() or
// // hashCode().
//
// // I can't imagine why containsAll would ever call equals on the parent collection,
// // so this should be safe from infinite call loops.
//
// // Doing containsAll() both ways should ensure that duplicates are checked properly
// // without checking order, but it's going to likely be a little slow. You want fast?
// // Implement List or Set instead!
// return containsAll(that) && that.containsAll(this);
// }
//
// @Override public int hashCode() { return UnmodIterable.hashCode(this); }
// }
// ========================================= Instance =========================================
// Methods are listed in the same order as the javadocs.
/** Not allowed - this is supposed to be unmodifiable */
@Override @Deprecated default boolean add(E e) {
throw new UnsupportedOperationException("Modification attempted");
}
/** Not allowed - this is supposed to be unmodifiable */
@Override @Deprecated default boolean addAll(Collection extends E> c) {
throw new UnsupportedOperationException("Modification attempted");
}
/** Not allowed - this is supposed to be unmodifiable */
@Override @Deprecated default void clear() {
throw new UnsupportedOperationException("Modification attempted");
}
// I don't think that this should be implemented here. It's a core function so each implementation
// of the interface should implement it
// /**
// This is quick for sets O(1) or O(log n), but slow for Lists O(n).
//
// {@inheritDoc}
// */
// @Override default boolean contains(Object o) {
// for (Object item : this) {
// if (Objects.equals(item, o)) { return true; }
// }
// return false;
// }
/**
The default implementation of this method has O(this.size() + that.size()) or O(n) performance.
So even though contains() is impossible to implement efficiently for Lists, containsAll()
has a decent implementation (brute force would be O(this.size() * that.size()) or O(n^2) ).
{@inheritDoc}
*/
@Override default boolean containsAll(Collection> c) {
// Faster to create a HashSet and call containsAll on that because it's
// O(this size PLUS that size), whereas looping through both would be
// O(this size TIMES that size).
return ( (c == null) || (c.size() < 1) ) ? true :
(size() < 1) ? false :
// (ts instanceof Set) ? ((Set) ts).containsAll(c) :
// (ts instanceof Map) ? ((Map) ts).entrySet().containsAll(c) :
new HashSet<>(this).containsAll(c);
}
// You can't implement equals correctly for a Collection due to duplicates, ordering, and
// the fact that List.equals(other) and Set.equals(other) both return false when other is
// not an instance of List or Set. This interface just isn't meant to be instantiated.
//boolean equals(Object o)
//int hashCode()
/** {@inheritDoc} */
@Override default boolean isEmpty() { return size() == 0; }
/** An unmodifiable iterator {@inheritDoc} */
@Override
UnmodIterator iterator();
//default Stream parallelStream()
/** Not allowed - this is supposed to be unmodifiable */
@Override @Deprecated default boolean remove(Object o) {
throw new UnsupportedOperationException("Modification attempted");
}
/** Not allowed - this is supposed to be unmodifiable */
@Override @Deprecated default boolean removeAll(Collection> c) {
throw new UnsupportedOperationException("Modification attempted");
}
/** Not allowed - this is supposed to be unmodifiable */
@Override @Deprecated default boolean removeIf(Predicate super E> filter) {
throw new UnsupportedOperationException("Modification attempted");
}
/** Not allowed - this is supposed to be unmodifiable */
@Override @Deprecated default boolean retainAll(Collection> c) {
throw new UnsupportedOperationException("Modification attempted");
}
//int size()
//default Spliterator spliterator()
//default Stream stream()
/**
* This method goes against Josh Bloch's Item 25: "Prefer Lists to Arrays", but is provided for backwards
* compatibility in some performance-critical situations. If you really need an array, consider using the somewhat
* type-safe version of this method instead, but read the caveats first.
*
* {@inheritDoc}
*/
@Override default Object[] toArray() {
return this.toArray(new Object[size()]);
}
/**
* This method goes against Josh Bloch's Item 25: "Prefer Lists to Arrays", but is provided for backwards
* compatibility in some performance-critical situations. If you need to create an array (you almost always do)
* then the best way to use this method is:
*
* MyThing[] things = col.toArray(new MyThing[coll.size()]);
*
* Calling this method any other way causes unnecessary work to be done - an extra memory allocation and potential
* garbage collection if the passed array is too small, extra effort to fill the end of the array with nulls if it
* is too large.
*
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override default T[] toArray(T[] as) {
if (as.length < size()) {
as = (T[]) new Object[size()];
}
Iterator iter = iterator();
for (int i = 0; i < size(); i++) {
as[i] = (T) iter.next();
}
if (size() < as.length) {
Arrays.fill(as, size(), as.length, null);
}
return as;
}
}