有没有人知道Java / JVM数据结构库提供熟悉的Java数据结构的功能(在功能意义上是不可变的,或“持久的”)?
“功能”是指对象本身是不可变的,而对这些对象的修改会返回与父对象在适当位置共享相同内部的新对象(为了提高时间和空间的效率;一个天真的实现可以复制每一次写作都是如此。
就像Java的并发库一样,这似乎不是我能够或应该自己实现的东西,所以拥有一个我可以在JVM中使用的功能数据结构库会很不错。
答案 0 :(得分:12)
Clojure的不可变和持久数据结构已被提取为Java库。您可以在http://github.com/krukow/clj-ds找到它们。这些数据结构不依赖于Clojure运行时,因此可以在应用程序的类路径中使用clojure.jar
。它们已被广泛使用,可以使用Java代码顺利运行。
请注意,使用这些不可变数据结构可能不是Java中的惯用语。
github页面没有可供下载的jar。您必须自己检查源并自行构建jar。
答案 1 :(得分:6)
Functional和immutable是大多数Scala集合库的核心属性。 Scala编译到JVM并与Java很好地互操作。 Scala语法也比Clojure(Lisp语法)更接近Java。
以下是Scala集合API的介绍页面。 http://www.scala-lang.org/docu/files/collections-api/collections.html
答案 2 :(得分:6)
试试Functional Java。它包含不可变的地图,集,列表和树。但是,这个库不仅仅是一组不可变数据结构!
答案 3 :(得分:3)
尝试使用Guava,它有不可变的map,list,set。它还有一些实用程序来支持不可变集合,而不是修改底层对象,返回一个新对象。
答案 4 :(得分:3)
我可以理解为什么并发类难以编写:很容易在那里看到难以看到的错误。
在编写不可变Collection
类时,Java有一种避免此类错误的好方法:每种Collection
都有a method similar to java.util.Collections.unmodifiableSet(someSet)
,这将为您提供一个包装器,让您可以看到底层{ {1}}但阻止所有变异方法。但它只是一个包装器:如果您保持对它的引用,您仍然可以更改基础Collection
,所以不要这样做。此外,立即克隆并包装来自控件之外的任何Collection
,因为交给他们的程序员可能会在以后修改它们,改变你不错的不可变数据。
Collection
的示例,其中包含所有必要的突变预防。
通过查看FunctionalHashSet
的API列表(不要忘记Set
),我创建了一个抽象超类。对于非突变方法,我只是将它们传递给基础toString
。对于变异方法,我抛出Set
并提供其他功能风格的方法。
这是抽象类UnsupportedOperationException
:
FunctionalSet
在实施过程中,没有什么可做的。我提供了一些构造函数和实用程序方法,并简单地使用了所有变异方法的默认实现。
这是一个实现,import java.util.Collections;
import java.util.Collection;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
public abstract class FunctionalSet<E> implements Set<E> {
// final to prevent mutations through reassignment.
protected final Set<E> set;
// private to prevent any use of the default constructor.
private FunctionalSet()
{ this.set = null; }
// unmodifiableSet to prevent mutations through Iterator and in subclasses.
protected FunctionalSet(final Set<E> set)
{ this.set = Collections.unmodifiableSet(set); }
public abstract FunctionalSet<E> clone();
public abstract FunctionalSet<E> fAdd(final E element);
public abstract FunctionalSet<E> fAddAll(final Collection<? extends E> elements);
public abstract FunctionalSet<E> fRemove(final Object element);
public abstract FunctionalSet<E> fRemoveAll(final Collection<?> elements);
public abstract FunctionalSet<E> fRetainAll(final Collection<?> elements);
protected abstract FunctionalSet<E> newFSet(final Set<E> newSet);
protected abstract Set<E> newSet();
protected abstract Set<E> cloneSet();
protected final FunctionalSet<E> __fAdd(final E element) {
if (set.contains(element)) return this;
final Set<E> newSet = cloneSet();
newSet.add(element);
return newFSet(newSet);
}
protected final FunctionalSet<E> __fAddAll(final Collection<? extends E> elements) {
if (set.containsAll(elements)) return this;
final Set<E> newSet = cloneSet();
newSet.addAll(elements);
return newFSet(newSet);
}
protected final FunctionalSet<E> __fRemove(final Object element) {
if (!set.contains(element)) return this;
final Set<E> newSet = cloneSet();
newSet.remove(element);
return newFSet(newSet);
}
protected final Set<E> __fRemoveAll(final Collection<?> elements) {
boolean hasNone = true;
for (final Object element : elements) {
if (set.contains(element)) {
hasNone = false;
break;
}
}
if (hasNone) return this;
final Set<E> newSet = cloneSet();
newSet.removeAll(elements);
return newFSet(newSet);
}
@SuppressWarnings("unchecked")
protected final Set<E> __fRetainAll(final Collection<?> rawElements) {
final Set elements = rawElements instanceof Set ? (Set) rawElements : new HashSet(rawElements);
// If set is a subset of elements, we don't remove any of the elements.
if (set.size() <= elements.size() && elements.containsAll(set)) return this;
final Set<E> newSet = newSet();
for (final E element : set) {
if (elements.contains(element)) newSet.add(element);
}
return newFSet(newSet);
}
private final UnsupportedOperationException unsupported(final String call, final String goodCall) {
return new UnsupportedOperationException(
String.format(this.getClass().getName() + "s are immutable. Use %s instead of %s.", goodCall, call)
);
}
public final boolean add(final E element)
{ throw unsupported("add", "fAdd"); }
public final boolean addAll(final Collection<? extends E> elements)
{ throw unsupported("addAll", "fAddAll"); }
public final void clear()
{ throw unsupported("clear", "new " + this.getClass().getName() + "()"); }
public final boolean remove(final Object element)
{ throw unsupported("remove", "fRemove"); }
public final boolean removeAll(final Collection<?> elements)
{ throw unsupported("removeAll", "fRemoveAll"); }
public final boolean retainAll(final Collection<?> elements)
{ throw unsupported("retainAll", "fRetainAll"); }
public final boolean contains(final Object element)
{ return set.contains(element); }
public final boolean containsAll(final Collection<?> elements)
{ return set.containsAll(elements); }
public final boolean equals(final Object object)
{ return set.equals(object); }
public final int hashCode()
{ return set.hashCode(); }
public final boolean isEmpty()
{ return set.isEmpty(); }
public final Iterator<E> iterator()
{ return set.iterator(); }
public final int size()
{ return set.size(); }
public final Object[] toArray()
{ return set.toArray(); }
public final <E> E[] toArray(final E[] irrelevant)
{ return set.toArray(irrelevant); }
public final String toString()
{ return set.toString(); }
}
。 :
FunctionalHashSet
一些注意事项:
import java.util.Collection;
import java.util.Set;
import java.util.HashSet;
public final class FunctionalHashSet<E> extends FunctionalSet<E> implements Cloneable {
public static final FunctionalHashSet EMPTY = new FunctionalHashSet();
public FunctionalHashSet()
{ super(new HashSet<E>()); }
public FunctionalHashSet(final HashSet<E> set)
{ this(set, true); }
@SuppressWarnings("unchecked")
private FunctionalHashSet(final HashSet<E> set, final boolean clone)
{ super(clone ? (HashSet<E>) set.clone() : set); }
public FunctionalHashSet(final Collection<E> elements)
{ this(new HashSet<E>(elements)); }
protected FunctionalHashSet<E> newFSet(final Set<E> newSet)
{ return new FunctionalHashSet<E>((HashSet<E>) newSet, false); }
protected HashSet<E> newSet()
{ return new HashSet<E>(); }
@SuppressWarnings("unchecked")
protected HashSet<E> cloneSet()
{ return new HashSet<E>(set); }
public FunctionalHashSet<E> clone()
{ return this; }
public FunctionalHashSet<E> fAdd(final E element)
{ return (FunctionalHashSet<E>) __fAdd(element); }
public FunctionalHashSet<E> fAddAll(final Collection<? extends E> elements)
{ return (FunctionalHashSet<E>) __fAddAll(elements); }
public FunctionalHashSet<E> fRemove(final Object element)
{ return (FunctionalHashSet<E>) __fRemove(element); }
public FunctionalHashSet<E> fRemoveAll(final Collection<?> elements)
{ return (FunctionalHashSet<E>) __fRemoveAll(elements); }
public FunctionalHashSet<E> fRetainAll(final Collection<?> elements)
{ return (FunctionalHashSet<E>) __fRetainAll(elements); }
}
。
FunctionalSet
中,我只返回完全相同的clone
FunctionalSet
运行set
并声明java.util.Collections.unmodifiableSet
可以防止两个突变源:用户无法通过final
进行变异,并且实现者不会在其中意外变异的实施方式。您可以对此进行修改并稍微修改以支持其他Iterator
。
答案 5 :(得分:1)
即使您应用Collections.immutable()
pure4j提供了Clojure持久集合的修改版本(例如包括泛型)以及对象的编译时不变性检查,为您提供了集合无法更改的保证。
Cornelius Mund的项目,https://github.com/cornim/ClojureCollections还提供了clojure集合,没有元素不变性保证,如果这是你需要的。