我最近开始查看 GUAVA 的集合,ImmutableList
这似乎相当麻烦(使用构建器实例等)。是否有一个库可以模仿集合应该如何表现的更“自然”方式(scala 的http://www. scala-lang.org/api/current/index.html#scala.collection.immutable.List就是一个例子)。我想要一些允许添加/删除等但保留不变性的东西,并且可能出于性能的目的,重用旧列表的一部分。
5 回答
Goldman Sachs Collection 库中有一个 ImmutableArrayList(和其他不可变集合)
请参阅ImmutableArrayList.java方法 newWith(T t) 和 newWithout(T t)
如果我理解正确,您需要一个不可变列表,该列表具有方便的添加/删除方法,这些方法返回尽可能多地重用原始列表结构的新列表实例。你可以这样做:
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()
.
请注意,这不是一个特别出色的实现;新元素被附加到列表的开头,而不是结尾。但也许这会让你知道从哪里开始。
您的要求表明您误解了这些工具的用途。不可变列表始终需要构建器,因为在调用构造器后它不能更改。这就是不可变对象的全部目的。他们永远不会改变。没有“设置阶段”。
如果您不需要,只需将现有列表包装在Collections.unmodifiableList()
.
当然,缺点是有人可能会保留对原始列表的引用并对其进行修改。或者黑客可能会窥探包装器,获取底层列表并开始对其进行处理。
不可变的意思是不变的,所以答案是否定的。你不能改变一个不可变的列表(哇!)。只要您只想使用一些内容初始化列表并将其保留,列表实用程序附带的构建器就很好,因此我建议您查看应用程序的架构。
关于旧列表的重用部分 - 如果列表是可变的,那么,通过改变它,您将冒着当前列表的状态不变性的风险。但是,如果实例都是不可变的,那么您实际上确实参考了前面的列表,因为您可以重复使用它的部分,只要它不改变,并且不可变的项目永远不会改变。