java Java中的持久化数据结构
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/734110/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Persistent data structures in Java
提问by ordnungswidrig
Does anyone know a library or some at least some research on creating and using persistent data structures in Java? I don't refer to persistence as long term storage but persistence in terms of immutability (see Wikipedia entry).
有谁知道在 Java 中创建和使用持久数据结构的库或至少一些研究吗?我不将持久性称为长期存储,而是将持久性称为不变性(请参阅维基百科条目)。
I'm currently exploring different ways to model an api for persistent structures. Using builders seems to be a interesting solution:
我目前正在探索为持久结构建模 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("Hyman");
Person p3 = Builder.update(p)
.withAddress(Builder.update(p.address())
.withCity("Berlin")
.build)
.build();
But this still feels somewhat boilerplated. Any ideas?
但这仍然感觉有些样板。有任何想法吗?
采纳答案by Tom Hawtin - tackline
I guess the obvious choices are:
我想显而易见的选择是:
o Switch to a transient data structure (builder) for the update. This is quite normal. StringBuilderfor Stringmanipulation for example. As your example.
o 切换到用于更新的瞬态数据结构(构建器)。这是很正常的。例如,StringBuilder用于String操纵。作为你的例子。
Person p3 =
Builder.update(p)
.withAddress(
Builder.update(p.address())
.withCity("Berlin")
.build()
)
.build();
o Always use persistent structures. Although there appears to be lots of copying, you should actually be sharing almost all state, so it is nowhere near as bad as it looks.
o 始终使用持久结构。虽然看起来有很多复制,但实际上你应该共享几乎所有的状态,所以它远没有看起来那么糟糕。
final Person p3 = p
.withAddress(
p.address().withCity("Berlin")
);
o Explode the data structure into lots of variables and recombine with one huge and confusing constructor.
o 将数据结构分解为许多变量,然后用一个庞大而混乱的构造函数重新组合。
final Person p3 = Person.of(
p.name(),
Address.of(
p.house(), p.street(), "Berlin", p.country()
),
p.x(),
p.y(),
p.z()
);
o Use call back interfaces to provide the new data. Even more boilerplate.
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 Use nasty hacks to make fields transiently available to code.
o 使用讨厌的技巧使字段暂时可用于代码。
final Person p3 = new PersonExploder(p) {{
a = new AddressExploder(a) {{
city = "Berlin";
}}.get();
}}.get();
(Funnily enough I was just put down a copy of Purely Functional Data Structures by Chris Okasaki.)
(有趣的是,我刚刚放下了 Chris Okasaki 写的纯函数数据结构的副本。)
回答by Juliet
Builders will make your code too verbose to be usable. In practice, almost all immutable data structures I've seen pass in state through the constructor. For what its worth, here are a nice series of posts describing immutable data structures in C# (which should convert readily into Java):
构建器会使您的代码过于冗长而无法使用。实际上,我见过的几乎所有不可变数据结构都通过构造函数传递状态。对于它的价值,这里有一系列很好的文章描述了 C# 中的不可变数据结构(应该很容易转换成 Java):
- Part 1: Kinds of Immutability
- Part 2: Simple Immutable Stack
- Part 3: Covariant Immutable Stack
- Part 4: Immutable Queue
- Part 5: Lolz!(included for completeness)
- Part 6: Simple Binary Tree
- Part 7: More on Binary Trees
- Part 8: Even More on Binary Trees
- Part 9: AVL Tree Implementation
- Part 10: Double-ended Queue
- Part 11: Working Double-ended Queue Implementation
- 第 1 部分:各种不变性
- 第 2 部分:简单的不可变堆栈
- 第 3 部分:协变不可变堆栈
- 第 4 部分:不可变队列
- 第 5 部分:洛兹!(包括完整性)
- 第 6 部分:简单的二叉树
- 第 7 部分:更多关于二叉树
- 第 8 部分:关于二叉树的更多信息
- 第 9 部分:AVL 树实现
- 第 10 部分:双端队列
- 第 11 部分:工作双端队列实现
C# and Java are extremely verbose, so the code in these articles is quite scary. I recommend learning OCaml, F#, or Scala and familiarizing yourself with immutability with those languages. Once you master the technique, you'll be able to apply the same coding style to Java much more easily.
C#和Java极其冗长,所以这些文章中的代码相当吓人。我建议学习 OCaml、F# 或 Scala,并熟悉这些语言的不变性。一旦掌握了该技术,您将能够更轻松地将相同的编码风格应用于 Java。
回答by Apocalisp
Have a look at Functional Java. Currently provided persistent datastructures include:
看看Functional Java。目前提供的持久化数据结构包括:
- Singly-linked list (fj.data.List)
- Lazy singly-linked list (fj.data.Stream)
- Nonempty list (fj.data.NonEmptyList)
- Optional value (a container of length 0 or 1) (fj.data.Option)
- Set (fj.data.Set)
- Multi-way tree (a.k.a. rose tree) (fj.data.Tree)
- Immutable map (fj.data.TreeMap)
- Products (tuples) of arity 1-8 (fj.P1..P8)
- Vectors of arity 2-8 (fj.data.vector.V2..V8)
- Pointed list (fj.data.Zipper)
- Pointed tree (fj.data.TreeZipper)
- Type-safe, generic heterogeneous list (fj.data.hlist.HList)
- Immutable arrays (fj.data.Array)
- Disjoint union datatype (fj.data.Either)
- 单链表(fj.data.List)
- 懒惰单向链表 (fj.data.Stream)
- 非空列表 (fj.data.NonEmptyList)
- 可选值(长度为 0 或 1 的容器)(fj.data.Option)
- 设置 (fj.data.Set)
- 多路树(又名玫瑰树)(fj.data.Tree)
- 不可变映射(fj.data.TreeMap)
- arity 1-8 (fj.P1..P8) 的乘积(元组)
- 2-8 元向量 (fj.data.vector.V2..V8)
- 尖头列表(fj.data.Zipper)
- 尖头树(fj.data.TreeZipper)
- 类型安全的通用异构列表 (fj.data.hlist.HList)
- 不可变数组 (fj.data.Array)
- 不相交联合数据类型 (fj.data.Either)
A number of usage examples are provided with the binary distribution. The source is available under a BSD license from Google Code.
二进制分发版提供了许多使用示例。该源代码在Google Code的 BSD 许可下可用。
回答by mikera
I implemented a few persistent data structures in Java. All open source (GPL) on Google code for anyone who is interested:
我用 Java 实现了一些持久化数据结构。任何感兴趣的人都可以在 Google 代码上使用所有开源 (GPL):
http://code.google.com/p/mikeralib/source/browse/#svn/trunk/Mikera/src/mikera/persistent
http://code.google.com/p/mikeralib/source/browse/#svn/trunk/Mikera/src/mikera/persistent
The main ones I have so far are:
到目前为止,我拥有的主要是:
- Persistent mutable test object
- Persistent hash maps
- Persistent vectors/lists
- Persistent sets (including a specialised persistent set of ints)
- 持久可变测试对象
- 持久哈希映射
- 持久向量/列表
- 持久集(包括专门的持久整数集)
回答by dfa
Follow a very simple tentative with dynamic proxy:
遵循一个非常简单的动态代理尝试:
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;
}
}
usage:
用法:
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);
}
}
grow directions:
成长方向:
- naming policy (getName, withName, name)
- caching toString(), hashCode()
- equals() implementations should be straightforward (although not implemented)
- 命名策略(getName、withName、name)
- 缓存 toString(), hashCode()
- equals() 实现应该很简单(虽然没有实现)
hope it helps :)
希望能帮助到你 :)
回答by Ingo
It is very difficult, if not impossible, to make things immutable that ain't designed so.
要使并非如此设计的事物保持不变是非常困难的,如果不是不可能的话。
If you can design from ground up:
如果您可以从头开始设计:
- use only final fields
- do not reference non immutable objects
- 仅使用最终字段
- 不要引用非不可变对象
回答by Pat
Do you want immutability :
你想要不变性:
- so external code cannot change the data?
- so once set a value cannot be changed?
- 所以外部代码不能改变数据?
- 所以一旦设置了一个值就不能改变?
In both cases there are easier ways to accomplish the desired result.
在这两种情况下,都有更简单的方法来实现所需的结果。
Stopping external code from changing the data is easy with interfaces:
使用接口可以轻松阻止外部代码更改数据:
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);
}
Then to stop changes to a value once set is also "easy" using java.util.concurrent.atomic.AtomicReference (although hibernate or some other persistence layer usage may need to be modified):
然后使用 java.util.concurrent.atomic.AtomicReference 停止对设置的值的更改也是“容易”的(尽管可能需要修改休眠或其他一些持久层的用法):
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....
}
But why do you need anything more than just interfaces to accomplish the task?
但是为什么您需要的不仅仅是接口来完成任务呢?
回答by Thumbnail
Google Guava now hosts a variety of immutable/persistent data structures.
Google Guava 现在托管了各种不可变/持久数据结构。

