2009-04-09 123 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()方法的實現應該是簡單(雖然未實現)

希望它能幫助:)