有没有人知道库或者至少有一些关于在Java中创建和使用持久数据结构的研究?我没有将持久性称为长期存储,而是将持久性称为不变性(参见Wikipedia entry)。
我目前正在探索为持久性结构建模api的不同方法。使用构建器似乎是一个有趣的解决方案:
// create persistent instance
Person p = Builder.create(Person.class)
.withName("Joe")
.withAddress(Builder.create(Address.class)
.withCity("paris")
.build())
.build();
// change persistent instance, i.e. create a new one
Person p2 = Builder.update(p).withName("Jack");
Person p3 = Builder.update(p)
.withAddress(Builder.update(p.address())
.withCity("Berlin")
.build)
.build();
但这仍然有点像锅炉板。有什么想法吗?
答案 0 :(得分:11)
构建器会使您的代码过于冗长而无法使用。在实践中,我见过的几乎所有不可变数据结构都通过构造函数传递状态。值得一提的是,这里有一系列很好的帖子描述了C#中的不可变数据结构(它应该很容易转换成Java):
C#和Java非常冗长,因此这些文章中的代码非常可怕。我建议学习OCaml,F#或Scala,并熟悉这些语言的不变性。掌握了这项技术后,您将能够更轻松地将相同的编码风格应用于Java。
答案 1 :(得分:6)
我想明显的选择是:
o切换到临时数据结构(构建器)以进行更新。这很正常。 StringBuilder
String
用于Person p3 =
Builder.update(p)
.withAddress(
Builder.update(p.address())
.withCity("Berlin")
.build()
)
.build();
操作。作为你的榜样。
final Person p3 = p
.withAddress(
p.address().withCity("Berlin")
);
o始终使用持久性结构。虽然看起来有很多复制,但实际上你应该分享几乎所有的状态,所以它远远不如看起来那么糟糕。
final Person p3 = Person.of(
p.name(),
Address.of(
p.house(), p.street(), "Berlin", p.country()
),
p.x(),
p.y(),
p.z()
);
o将数据结构分解为大量变量,并与一个庞大而混乱的构造函数重新组合。
final Person p3 = Person.of(new PersonInfo(
public String name () { return p.name(); )
public Address address() { return Address.of(new AddressInfo() {
private final Address a = p.address();
public String house () { return a.house() ; }
public String street () { return a.street() ; }
public String city () { return "Berlin" ; }
public String country() { return a.country(); }
})),
public Xxx x() { return p.x(); }
public Yyy y() { return p.y(); }
public Zzz z() { return p.z(); }
});
o使用回调接口提供新数据。更多样板。
final Person p3 = new PersonExploder(p) {{
a = new AddressExploder(a) {{
city = "Berlin";
}}.get();
}}.get();
o使用讨厌的黑客来使字段暂时可用于代码。
{{1}}
(有趣的是,我刚刚放下了Chris Okasaki的Purely Functional Data Structures副本。)
答案 2 :(得分:6)
看看Functional Java。目前提供的持久性数据结构包括:
二进制分发提供了许多用法示例。该来源可通过Google Code的BSD许可证获得。
答案 3 :(得分:5)
我在Java中实现了一些持久性数据结构。 Google代码上的所有开源(GPL)适用于任何感兴趣的人:
http://code.google.com/p/mikeralib/source/browse/#svn/trunk/Mikera/src/mikera/persistent
到目前为止我的主要内容是:
答案 4 :(得分:3)
使用动态代理跟随一个非常简单的暂定:
class ImmutableBuilder {
static <T> T of(Immutable immutable) {
Class<?> targetClass = immutable.getTargetClass();
return (T) Proxy.newProxyInstance(targetClass.getClassLoader(),
new Class<?>[]{targetClass},
immutable);
}
public static <T> T of(Class<T> aClass) {
return of(new Immutable(aClass, new HashMap<String, Object>()));
}
}
class Immutable implements InvocationHandler {
private final Class<?> targetClass;
private final Map<String, Object> fields;
public Immutable(Class<?> aTargetClass, Map<String, Object> immutableFields) {
targetClass = aTargetClass;
fields = immutableFields;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("toString")) {
// XXX: toString() result can be cached
return fields.toString();
}
if (method.getName().equals("hashCode")) {
// XXX: hashCode() result can be cached
return fields.hashCode();
}
// XXX: naming policy here
String fieldName = method.getName();
if (method.getReturnType().equals(targetClass)) {
Map<String, Object> newFields = new HashMap<String, Object>(fields);
newFields.put(fieldName, args[0]);
return ImmutableBuilder.of(new Immutable(targetClass, newFields));
} else {
return fields.get(fieldName);
}
}
public Class<?> getTargetClass() {
return targetClass;
}
}
用法:
interface Person {
String name();
Person name(String name);
int age();
Person age(int age);
}
public class Main {
public static void main(String[] args) {
Person mark = ImmutableBuilder.of(Person.class).name("mark").age(32);
Person john = mark.name("john").age(24);
System.out.println(mark);
System.out.println(john);
}
}
成长方向:
希望它有所帮助:)
答案 5 :(得分:1)
如果不是不可能的话,将事物设计成不可变的是非常困难的。
如果你可以从头开始设计:
答案 6 :(得分:0)
你想要不变性吗?
在这两种情况下,都有更简单的方法来实现预期的结果。
使用接口可以轻松停止外部代码更改数据:
public interface Person {
String getName();
Address getAddress();
}
public interface PersonImplementor extends Person {
void setName(String name);
void setAddress(Address address);
}
public interface Address {
String getCity();
}
public interface AddressImplementor {
void setCity(String city);
}
然后使用java.util.concurrent.atomic.AtomicReference(虽然可能需要修改hibernate或其他一些持久层使用),一旦设置也很容易停止对值的更改:
class PersonImpl implements PersonImplementor {
private AtomicReference<String> name;
private AtomicReference<Address> address;
public void setName(String name) {
if ( !this.name.compareAndSet(name, name)
&& !this.name.compareAndSet(null, name)) {
throw new IllegalStateException("name already set to "+this.name.get()+" cannot set to "+name);
}
}
// .. similar code follows....
}
但为什么你需要的不仅仅是接口来完成任务呢?
答案 7 :(得分:0)
Google Guava现在拥有各种immutable/persistent data structures。