我最近开始查看GUAVA的集合,即ImmutableList
,这看起来相当麻烦(使用构建器实例等)。是否有一个库可以模仿集合应该如何表现的更“自然”的方式(scala的http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.List就是一个例子)。我想要一些允许添加/删除等但保留不变性的东西,并且可能出于性能目的,重用旧列表的部分内容。
答案 0 :(得分:3)
Goldman Sachs Collection library
中有一个ImmutableArrayList(和其他不可变集合)参见ImmutableArrayList.java,newWith(T t)和newWithout(T t)方法
答案 1 :(得分:1)
如果我理解正确,您需要一个具有方便的添加/删除方法的不可变列表,这些方法返回新的列表实例,这些实例尽可能多地重用原始列表结构。你可以这样做:
public abstract class ImmutableList<T> implements Iterable<T> {
/**
* Adds an element to the head of the list, returning the new list.
*
* @param o The element to be added to the list.
* @return The list consisting of the element <var>o</var> followed by
* this list.
*/
public final ImmutableList<T> add(final T o) {
return new Node<>(o, this);
}
/**
* Removes the element <var>o</var> resulting in a new list which
* is returned to the caller.
*
* @param o The object to be removed from the list.
* @return A list consisting of this list with object <var>o</var> removed.
*/
public abstract ImmutableList<T> remove(final T o);
public abstract boolean isEmpty();
public abstract int size();
public abstract boolean contains(final T o);
private ImmutableList() {}
/**
* Returns a "standard" enumeration over the elements of the list.
*/
public Iterator<T> iterator() {
return new NodeIterator<>(this);
}
/**
* The empty list. Variables of type ImmutableList should be
* initialised to this value to create new empty lists.
*/
private static final ImmutableList<?> EMPTY = new ImmutableList<Object>() {
@Override
public ImmutableList<Object> remove(final Object o) {
return this;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public int size() {
return 0;
}
@Override
public boolean contains(final Object o) {
return false;
}
};
@SuppressWarnings("unchecked")
public static <T> ImmutableList<T> empty() {
return (ImmutableList<T>)EMPTY;
}
public static <T> ImmutableList<T> create(final T head) {
return new Node<>(head, ImmutableList.<T>empty());
}
static class Node<T> extends ImmutableList<T> {
private final int _size;
private Node(final T element, final ImmutableList<T> next) {
_element = element;
_next = ArgumentHelper.verifyNotNull(next, "next");
_size = next.size() + 1;
}
public ImmutableList<T> remove(final T old) {
if (_element == old) {
return _next;
}
else {
final ImmutableList<T> n = _next.remove(old);
if (n == _next) {
return this;
}
else {
return new Node<>(_element, n);
}
}
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public int size() {
return _size;
}
@Override
public boolean contains(final T o) {
return Objects.equals(_element, o) || _next.contains(o);
}
private final T _element;
private final ImmutableList<T> _next;
}
private class NodeIterator<T> implements Iterator<T> {
private ImmutableList<T> _current;
private NodeIterator(final ImmutableList<T> head) {
_current = ArgumentHelper.verifyNotNull(head, "head");
}
public boolean hasNext() {
return !_current.isEmpty();
}
public T next() {
final T result = ((Node<T>)_current)._element;
_current = ((Node<T>)_current)._next;
return result;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}
对于此实现,通过将项添加到ImmutableList.empty()
来构建新列表。
请注意,这不是一个特别好的实现;新元素将附加到列表的开头,而不是结尾。但也许这会让你知道从哪里开始。
答案 2 :(得分:0)
您的要求表明您误解了这些工具的用途。不可变列表总是需要构建器,因为在调用构造函数后它不能更改。这就是不可改变的全部目的。他们永远不会改变。没有“设置阶段。”
如果您不需要,只需将现有列表包装在Collections.unmodifiableList()
。
当然,缺点是某人可能会保留对原始列表的引用并对其进行修改。或者黑客可能会查看包装器,获取基础列表并开始搞乱它。
答案 3 :(得分:0)
Clojure包含这样的不可变(或持久)collections。
简单地说,添加或删除新元素会返回一个新集合,通常 通过巧妙使用Trie类型数据结构来重用旧集合的大部分内容。
就他们自己而言,这些不适合直接使用Java。
Pure4j试图将这些(以及Clojure提倡的基于不可变/值的样式)移植到Java语言中。这可能是你追求的目标。
免责声明:我是Pure4J的开发者
答案 4 :(得分:-1)
不可变意味着不改变的,所以答案是否定的。你不能改变一个不可变的列表(哇!)。只要您想要使用某些内容初始化列表并将其保留在列表实用程序中,那么随列表实用程序一起提供的构建器很好,因此我建议您查看应用程序的体系结构。
关于旧列表的重用部分 - 如果列表是可变的,那么通过对其进行变异,您将冒着导出当前列表的状态的不变性的风险。但是,如果实例都是不可变的,那么实际上做引用前面的列表,因为只要它没有改变就可以重用它的部分,并且不可变的项不会改变。