简体   繁体   English

如何在Java中构建复杂的,层次结构的不可变数据结构?

[英]How to build a complex, hierarchic immutable data structure in Java?

I'm building a Java library for a customer, and one of the things they want is a data representation of a particular set of standards they work with. 我正在为客户构建一个Java库,他们想要的东西之一就是他们使用的一组特定标准的数据表示。 I don't want to reveal my customer's interests, but if he were an alchemist, he might want the following: 我不想透露客户的利益,但如果他是炼金术士,他可能会想要以下内容:

Elements
  Fire
    Name="Fire"
    Physical
      Temperature=451
      Color="Orange"
    Magical
      Domain="Strength"
  Water
    Name="Water"
    Physical
      Color="Blue"
  Earth
    Name="Earth"
    Magical
      Domain="Stability"
      Ordinality=1

I need to be able to access various data elements by name, such as: 我需要能够按名称访问各种数据元素,例如:

  Elements.Earth.Name
  Elements.Water.Physical.Color

I also need to be able to iterate through attributes, as: 我还需要能够遍历属性,如下所示:

  for (MagicalType attrib : Elements.Fire.Magical)
  {
    ...
  }

I have actually been able to create this data structure, and I can do everything I've asked for above -- though I had to create separate arrays for the iteration, so really what I do looks more like: 我实际上已经能够创建这个数据结构了,我可以做我上面要求的所有事情 - 虽然我不得不为迭代创建单独的数组,所以我真的看起来更像是:

  for (MagicalType attrib : Elements.Fire.MagicalAuxArray)

Unfortunately I haven't been able to meet my last requirement: the entire data structure must be immutable. 不幸的是,我无法满足我的最后要求:整个数据结构必须是不可变的。 I have tried repeatedly, and scoured the web looking for examples, but so far I haven't been able to accomplish this in any reasonable manner. 我已经反复尝试过,并在网上搜索示例,但到目前为止,我还没能以任何合理的方式完成此任务。 Note that the final data structure will be quite large; 请注意,最终的数据结构将非常大; I'm really hoping to avoid a solution that is too repetitious or creates too many public symbols. 我真的希望避免一个过于重复或创造太多公共符号的解决方案。

I am a very experienced programmer, less experienced with Java. 我是一名非常有经验的程序员,对Java不太熟悉。 Can anyone suggest how I might represent the above data to meet all my requirements? 任何人都可以建议我如何代表上述数据以满足我的所有要求吗?

A few ways that come to mind immediately: 立即浮现在脑海中的几种方式:

  • Don't provide setter methods for your object. 不要为对象提供setter方法。 You users can only create the object via a constructor and once created, it cannot be modified. 用户只能通过构造函数创建对象,一旦创建,就无法修改。 This goes for other state-modification methods as well. 这也适用于其他状态修改方法。 If you want to avoid a very large parameter-list in your constructor, you can use the Builder pattern (described in Effective Java by Joshua Bloch (2nd Ed) ) 如果要在构造函数中避免使用非常大的参数列表,可以使用Builder模式 (在Joshua Bloch(第2版)中的Effective Java中描述)
  • When returning collections, make defensive copies. 返回集合时,制作防御性副本。 In this case use a List instead of an array. 在这种情况下,使用List而不是数组。 That way you can do return new ArrayList<MagicalType>(MagicalAuxList) instead of return MagicalAuxList . 这样你就可以return new ArrayList<MagicalType>(MagicalAuxList)而不是return MagicalAuxList This way people who use the class won't be able to modify the collection. 这样,使用该类的人将无法修改该集合。 One caveat here. 一点需要注意。 If your array contains complex objects, they must be immutable as well. 如果您的数组包含复杂对象,则它们也必须是不可变的。
  • For immutable collections, you can also try using the unmodifiableCollection static method (there are similar static-methods for lists, sets, etc. - use whichever one is appropriate for you) to convert your collection when you return it. 对于不可变集合,您还可以尝试使用unmodifiableCollection静态方法(对于列表,集合等有类似的静态方法 - 使用适合您的任何一种)在您返回时转换您的集合。 This is an alternative to defensive copying. 这是防御性复制的替代方案。

Why do you use arrays? 为什么使用数组? Wouldn't immutable collections (eg from Google Guava ) do a better job? 不可变的集合(例如来自Google Guava )不会做得更好吗?

You can use Iterable in your public API. 您可以在公共API中使用Iterable Cleaner than Collections with all the mutators that you have to suppress. 使用所有需要抑制的变异器来清除收藏。 (unfortunately Iterator has a remove() method(?!) but that's just one) (遗憾的是Iterator有一个remove()方法(?!),但那只是一个)

public final Iterable<MagicalType> magics;

for(MagicalType magic : magics) ...

you could try the code below that uses final, enums and unmodifiable maps. 您可以尝试使用final,enums和unmodifiable maps的下面的代码。 but that does not let you access by name since you need to do a get from the map. 但是这不允许您按名称访问,因为您需要从地图中获取。 you could probably do that in groovy. 你可以在groovy中做到这一点。

import java.util.*;

enum Color {
    red, green, blue;
}

class Physical {
    Physical(final Double temperature, final Color color) {
        this.temperature = temperature;
        this.color = color;
        final Map<String, Object> map=new LinkedHashMap<String, Object>();
        map.put("temperature", temperature);
        map.put("color", color);
        this.map=Collections.unmodifiableMap(map);
    }

    final Double temperature;
    final Color color;
    final Map<String, Object> map;
}

class Magical {
    Magical(final String domain, final Integer ordinality) {
        this.domain = domain;
        this.ordinality = ordinality;
        final Map<String, Object> map=new LinkedHashMap<String, Object>();
        map.put("domain", domain);
        map.put("ordinality", ordinality);
        this.map=Collections.unmodifiableMap(map);
    }

    final String domain;
    final Integer ordinality;
    final Map<String, Object> map;
}

public enum Elements {
    earth("Earth", new Magical("Stability", 1), null), air("Air", null, null), fire("Fire", new Magical("Strength", null), new Physical(451., Color.red)), water(
            "Water", null, new Physical(null, Color.blue));
    Elements(final String name, final Magical magical, final Physical physical) {
        this.name = name;
        this.magical = magical;
        this.physical = physical;
    }

    public static void main(String[] arguments) {
        System.out.println(Elements.earth.name);
        System.out.println(Elements.water.physical.color);
        for (Map.Entry<String, Object> entry : Elements.water.physical.map.entrySet())
            System.out.println(entry.getKey() + '=' + entry.getValue());
        for (Map.Entry<String, Object> entry : Elements.earth.magical.map.entrySet())
            System.out.println(entry.getKey() + '=' + entry.getValue());
    }

    final String name;
    final Magical magical;
    final Physical physical;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM