2009-04-09 84 views
15

有没有人知道一个库或至少一些关于在Java中创建和使用持久化数据结构的研究?我并没有将持久性称为长期存储,而是将其作为长期存储(参见Wikipedia entry)。Java中的持久数据结构

我目前正在探索不同的方式来建模持久性结构的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(); 

但这仍然感觉有些boilerplated。有任何想法吗?

+2

不知道这里的功能编程标签是适当的。我使用函数式编程来表示Haskell和Lisp等语言。 :) – uriDium 2009-04-09 13:44:55

+7

我认为功能性,因为持久数据结构是通用的函数式编程语言。我猜他希望能够在Java中做一些他很容易用某种功能语言来做的事情。 为什么社会维基?这对我来说似乎是一个可靠的编程问题。 – 2009-04-09 15:03:26

回答

6

我想最明显的选择是:

O键的更新瞬时数据结构(制造商)。这很正常。例如StringBuilder代表String操作。作为你的榜样。

​​

o始终使用持久性结构。虽然似乎有很多复制,但实际上你应该共享几乎所有的状态,所以它看起来并没有那么糟糕。

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(); 

(有趣的是,我只是克里斯·奥卡萨基放下纯功能性数据结构的副本。)

+0

如果我们的更新方法融入迁延型一个甚至simplyfy这最后的人P3 = p2.withAddress(p3.address()。withCity(“柏林”)) 我很好奇,如果有一种方法来省略对“p3”的重复引用。 – ordnungswidrig 2009-04-09 14:38:46

+0

该示例不应该有建设者(更新)。你可以添加讨厌的黑客获取地址,然后回去的人,但是这将是邪恶和混乱。另一个令人讨厌的黑客应该是一个爆炸Person的类,然后使用双大括号成语 - 没有足够的字符来描述它。 – 2009-04-09 14:49:40

+0

只是注意到,双大括号初始化被称为“讨厌”的原因 - 它创建了一个子类(影响代码大小和性能),经常获得对封闭对象的引用(可能导致内存泄漏)。 – v6ak 2018-03-10 13:10:54

1

这是非常困难的,如果不是不可能的,让事情一成不变未设计成。

如果能够从地面向上设计:

  • 只使用final字段
  • 不引用非不可变对象
11

建设者将会使你的代码太冗长是可用的。实际上,我所看到的几乎所有不可变数据结构都通过构造函数传入状态。对于什么样的价值,这里是一个不错的一系列描述C#一成不变的数据结构的职位(这应该很容易转换成Java):

C#和Java是非常详细的,所以在这些文章中的代码是相当可怕的。我建议学习OCaml,F#或Scala,并熟悉这些语言的不变性。一旦掌握了技巧,您就可以更容易地将相同的编码风格应用于Java。

+1

你是什么意思“建设者会让你的代码冗长无法使用”?你的意思是说“太冗长可用”吗?如果是这样,我强烈反对。建设者使代码简单,易于理解和非常明确。 – kgrad 2009-04-09 14:25:40

0

你想不变性:

  1. 所以外部代码不能改变的数据?
  2. 所以一旦设置了一个值不能改变?

在这两种情况下,都有更简单的方法来实现所需的结果。

从改变数据停止外部代码是易与接口:

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中(尽管休眠或者一些其他的持久层的使用可能需要修改):

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.... 
} 

但是,为什么你有什么需要的不仅仅是接口来完成任务?

6

看一看Functional Java。目前提供持续的数据结构包括:

  • 单链表(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)

一些使用示例随二进制分发提供。来源可从Google Code获得BSD许可证。

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); 
    } 
} 

成长方向:

  • 命名策略(的getName,withName,名)
  • 缓存toString(),hashCode()
  • equals()方法的实现应该是简单(虽然未实现)

希望它能帮助:)