开发者

Two dimensional array/structure question

I need advice in organising flexible data structure in the program. Look, I've to organise data structure which at glance looks like two dimensional array like

double[][] arr = new double[10][10];

if you need to set or get some element of this array you'll write something like

double x=arr[i,j]; // arr[i,j]=y;

where i and j are int numbers

But in my program I need something like

double k=arr[obj1,obj2]

where obj1 and obj2 are objects

I've an idea - HashMap could solve my problem

So It'll looks like

class Coordinates {
      Object1 obj1;
      Object2 obj2;
      //TODO override equals() method
      Coordinates(Object1 obj1, Object开发者_运维知识库2 obj2) {
           this.obj1=obj1;
           this.obj2=obj2;
      }
}
...
HashMap<Coordinates, Double> semiTwoDimArray=new HashMap<Coordinates, Double>();

and setting and getting of elements would looks like

semiTwoDimArray.put(new Coordinates(obj1, obj2), 3.14);

and getting might be as

double pi = semiTwoDimArray.get(new Coordinates(obj1, obj2));

But as for me: 1) Such realisation has problems with iterating through row or column (too many operations) 2) Looks inelegant

Could anybody advice me better solution?


You could wrap the structure into a class and provide nice and clean get(Object,Object) and set(Object,Object,double) methods.

This way the inelegant code will be contained within the class and won't need to be repeated every time you access the structure.

This will also make it easy to change the underlying data structure if you find the original choice to be sub-optimal.


As you remarked, the HashMap<Coordinate, Value> structure does not let you iterate through the rows or columns.

If this is needed, then this is certainly not the right data structure here.

Some ideas:

  • Use nested maps: Map<RowKey, Map<ColumnKey, Value>> or Map<ColumnKey, Map<RowKey, Value>> - one supports iterating through rows, the other through columns. Maybe you'll need to have both structures at the same time.

  • use one map for direct access, and additionally lists for iterating by row/column.

  • Use a data structure made for this purpose (I can imagine a 2D-hashmap now).

First you'll need to list your requirements, and maybe even analyze how many objects there are, how many in each column and row (the performance characteristics will depend on this). And do you have arbitrary objects, or are your objects of some key type? Is this type comparable?


Here is an example interface for what aix proposed:

package de.fencing_game.paul.examples;

import java.util.*;

/**
 * A map with two keys.
 *
 * @see java.util.Map
 *
 * @param <K1> the first key type.
 * @param <K2> the second key type.
 * @param <V> the value type.
 */
public interface Map2D<K1,K2,V> {

    /**
     * A pair of keys for our map.
     */
    public interface KeyPair<K1,K2> {
        public K1 getKey1();
        public K2 getKey2();
        /**
         * returns the hashCode of this entry.
         * The hashcode is defines as 
         * {@code  getKey1().hashCode() * 13 + getKey2().hashCode() * 17}
         * If one of these objects is {@code null}, replace 0 for
         * its hash code.
         */
        public int hashCode();

        /**
         * compares this entry to another object. An object {@code that} is
         * equal this entry, if and only if it is also an Map2D.KeyPair, and 
         * {@code
         *  this.getKey1().equals(that.getKey1()) &&
         *  this.getKey2().equals(that.getKey2());
         * }
         */
        public boolean equals(Object that);
    }


    /**
     * an entry in our map, consisting of two keys and a value.
     * @see Map.Entry
     */
    public interface Entry<K1,K2,V> {
        public K1 getKey1();
        public K2 getKey2();
        public V getValue();
        public V setValue(V val);
        /**
         * returns the hashCode of this entry.
         * The hashcode is defines as 
         * {@code  getKey1.hashCode() * 7 + getKey2().hashCode() * 3 +
         *         getValue().hashCode() * 5 }
         * If one of these objects is {@code null}, replace 0 for
         * its hash code.
         */
        public int hashCode();

        /**
         * compares this entry to another object. An object {@code that} is
         * equal this entry, if and only if it is also an Map2D.Entry, and 
         * {@code
         *  this.getKey1().equals(that.getKey1()) &&
         *  this.getKey2().equals(that.getKey2()) &&
         *  this.getValue().equals(that.getValue());
         * }
         */
        public boolean equals(Object that);
    }


    /**
     * the number of mappings in this map.
     */
    public int size();

    /**
     * returns true if this map is empty.
     */
    public boolean isEmpty();


    /**
     * returns true if this map contains a mapping for the specified key pair.
     */
    public boolean containsKeys(Object key1, Object key2);

    /**
     * returns true if this map contains a mapping with this
     * specified key as first key.
     */
    public boolean containsKey1(Object key1);
    /**
     * returns true if this map contains a mapping with this
     * specified key as second key.
     */
    public boolean containsKey2(Object key2);

    /**
     * returns true if this map contains a mapping with the
     * specified value.
     */
    public boolean containsValue(Object value);

    /**
     * retrieves an element from the map.
     * @return null if there is no such pair of keys, else the value.
     */
    public V get(K1 key1, K2 key2);

    /**
     * puts a new element in the map.
     * @return the old value, or null, if there was no such mapping.
     */
    public V put(K1 key1, K2 key2, V value);

    /**
     * removes a mapping from this map.
     */
    public V remove(Object key1, Object key2);

    /**
     * puts all mappings from the specified 2d-map into this map.
     */
    public void putAll(Map2D<? extends K1, ? extends K2, ? extends V> map);

    /**
     * clears this map.
     */
    public void clear();

    /**
     * returns a collection view of the values of this map.
     */
    public Collection<V> values();

    /**
     * returns a collection view of this map's entries.
     */
    public Set<Map2D.Entry<K1,K2,V>> entrySet();

    /**
     * returns a map view as a map of key-pairs to values.
     */
    public Map<KeyPair<K1,K2>, V> flattedMap();

    /**
     * returns a map view as a map of maps, by first key first.
     */
    public Map<K1, Map<K2, V>> projection1Map();

    /**
     * returns a map view as a map of maps, by second key first.
     */
    public Map<K2, Map<K1, V>> projection2Map();

    /**
     * compares this map with another object.
     * This Map2D is equal to another object only if it also is a Map2D
     * and these equivalent conditions hold:
     * <ul>
     *   <li>{@code this.entrySet().equals(that.entrySet())}</li>
     *   <li>{@code this.flattedMap().equals(that.flattedMap())}</li>
     *   <li>{@code this.projection1Map().equals(that.projection1Map())}</li>
     *   <li>{@code this.projection2Map().equals(that.projection2Map())}</li>
     * </ul>
     */
    public boolean equals(Object that);

    /**
     * The hashCode is defined as the {@link #entrySet()}{@code .hashCode()}.
     */
    public int hashCode();

}

I'll later add some example implementations.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜