java data-structure to simulate a data tree

2019-03-31 20:30发布

I need help defining what approach to use. I have a SOAP response giving me an xml file. I need to end-up with 3 correlated lists displayed on screen. When you select one item on the first list, the corresponding choices will appear on the second list etc. I am only interested on how to organize efficiently the data after it is extracted from the xml stream. Here's an xml snippet:

<device>
    <manufacturer>Acer</manufacturer>
    <model>A1</model>
    <platform>Android</platform>
</device>
<device>
    <manufacturer>Acer</manufacturer>
    <model>A1</model>
    <platform>J2ME</platform>
</device>
<device>
    <manufacturer>Acer</manufacturer>
    <model>A2</model>
    <platform>Android</platform>
</device>
<device>
    <manufacturer>Samsung</manufacturer>
    <model>E400</model>
    <platform>Android</platform>
</device>

So, I will have something like manufacturer={"Acer", "Acer", "Acer","Samsung"}, model={"A1","A1", "A2", "E400"}, platform={"Android","J2ME","Android","Android"}.

Here comes the fun part: I need to massage the data so that I can use it to display 3 lists. After selecting Android, Acer and Samsung become available. If Acer is selected, then model A1 and A2 are available. All lists need to be sorted. Currently I'm using Sax to parse the data into a vector of objects, containing manufacturer, model, platform fields. All I can think of is a TreeMap like structure. Any suggestions would be appreciated.

3条回答
何必那么认真
2楼-- · 2019-03-31 21:05

I'd just use a sortable collection of custom objects and then filter that collection based on predicates. I am using Guava for all of this, but there are of course other (usually more complicated) ways to implement this.

Here's my Product Object:

public class Product implements Comparable<Product>{

    private final String manufacturer;
    private final String model;
    private final String platform;

    public Product(final String manufacturer,
        final String model,
        final String platform){
        this.manufacturer = manufacturer;
        this.model = model;
        this.platform = platform;
    }

    public String getManufacturer(){
        return manufacturer;
    }

    public String getModel(){
        return model;
    }

    public String getPlatform(){
        return platform;
    }

    @Override
    public int hashCode(){
        return Objects.hashCode(manufacturer, model, platform);
    }

    @Override
    public boolean equals(final Object obj){
        if(obj instanceof Product){
            final Product other = (Product) obj;
            return Objects.equal(manufacturer, other.manufacturer)
                && Objects.equal(model, other.model)
                && Objects.equal(platform, other.platform);
        }
        return false;
    }

    @Override
    public int compareTo(final Product o){
        return ComparisonChain
            .start()
            .compare(manufacturer, o.manufacturer)
            .compare(model, o.model)
            .compare(platform, o.platform)
            .result();
    }

}

Now I'd just use a TreeSet<Product> and apply views on it. Here's a sample method that returns a live view that is filtered by model:

public static Collection<Product> filterByModel(
    final Collection<Product> products,
    final String model){
    return Collections2.filter(products, new Predicate<Product>(){

        @Override
        public boolean apply(final Product product){
            return product.getModel().equals(model);
        }
    });
}

Use it like this:

Collection<Product> products = new TreeSet<Product>();
// add some products
Collection<Product> filtered = filterByModel(products, "A1");

Update: We can take it even further, using only one collection, backed by chained predicates that are in turn tied to a model backed by your view. Brain hurts? Check this out:

// this is the collection you sent to your view
final Collection<Product> visibleProducts =
    Collections2.filter(products, Predicates.and(Arrays.asList(
        new ManufacturerPredicate(yourViewModel),
        new ModelPredicate(yourViewModel),
        new PlatformModel(yourViewModel)))
);

yourViewModel is an object that is backed by the values returned from your form controller. Each predicate uses a field of this model object to decide whether it applies or not.

e.g. The ModelPredicate checks all products in the collection to see whether their model is among the selected ones. Since this uses and logic, you can make it a hierarchic structure (if the manufacturer predicate returns false, the model and platform predicates are never called).

查看更多
Deceive 欺骗
3楼-- · 2019-03-31 21:21

I do not think that hierarchical structure is what you need here. Because user may select first platform or manufacturer. If he selects first Android you want to show 3 devices. If he selects first Acer he will see 2 devices.

So, my suggesting is the following.

  1. create class Device with properties manufacturer, model, platform.
  2. create a plain linked list that contains all these devices.
  3. Create 2 maps: manufaturerIndex and plarformIndex that look like:
    Map<String, Collection<Device>> manufacturerIndex;

  4. Iterate once over the list and populate all indexes maps.

Like this:

for(Device d : devices) {
    Collection<Device> selected = manufacturerIndex.get(d.getManufacturer());
    if (selected == null) {
         selected = new ArrayList<Device>();
         manufactuerIndex.put(d.getManufacturer(), selected);
    }
    selected.add(d);
    // the same for the second index
}

Now you can use the data structure.

manufactuerIndex.get("Nokia") -> returns all Nokia devices.

Pay attention that this data structure is extendable. You can always add as many indexes as you want.

查看更多
可以哭但决不认输i
4楼-- · 2019-03-31 21:28

I use nested maps for something like that. Use TreeMap to get sorted results:

TreeMap<String, TreeMap<String, Model> manufacturerMap;

TreeMap<String, Model> models = manufacturerMap.get( name );
if( models == null ) {
    models = new TreeMap<String, Model>();
    manufacturerMap.put( name. models );
}

... etc ...
查看更多
登录 后发表回答