views:

316

answers:

7

I've created the following structure which maps unique double values to one or more pairs of integers:

   @SuppressWarnings("boxing")
   private static final HashMap<Double, Integer[][]> rules =
      new HashMap<Double, Integer[][]>() {
         private static final long serialVersionUID = 1L;
         {
            put(-0.6, new Integer[][] { { 1, 3 } });
            put(-0.3, new Integer[][] { { 2, 2 } });
            put(0.0, new Integer[][] { { 2, 4 }, { 3, 3 }, { 4, 2 } });
            put(0.3, new Integer[][] { { 4, 4 } });
            put(0.6, new Integer[][] { { 5, 3 } });
         }
   };

Can I rewrite this so it's simpler - i.e not have to deal with warnings (serialVersionUID, boxing), and it being so verbose?

+2  A: 

Create another class to hold your pairs of integers, and store them using a list:

Map<Double,List<MyPair>>

Are these to be arbitrary pairs of integers, or will the represent something? If the latter, then name appropriately. New classes are cheap in Java, and good naming will reduce maintenance costs.

Edit: why are you creating an anonymous subclass of HashMap?

kdgregory
A: 

using a static initializer would be slightly better in my opinion, although it does nothing about the verbosity:

private static final Map<Double, int[][]> rules;

static {
    rules = new HashMap<Double, int[][]>();

    rules.put(-0.6, new int[][] { { 1, 3 } });
    rules.put(-0.3, new int[][] { { 2, 2 } });
    rules.put(0.0, new int[][] { { 2, 4 }, { 3, 3 }, { 4, 2 } });
    rules.put(0.3, new int[][] { { 4, 4 } });
    rules.put(0.6, new int[][] { { 5, 3 } });

}

Another option using a special Pair class and Arrays.asList:

class Pair<A, B> {
  A a;
  B b;

  public Pair(A fst, B snd) {
  }

  // getters and setters here
}

private static final Map<Double, List<Pair<Integer, Integer>>> rules;

static {
    rules = new HashMap<Double, List<Pair<Integer, Integer>>>();

    rules.put(-0.6, Arrays.asList(new Pair(1, 3)));
    rules.put(-0.3, Arrays.asList(new Pair(2, 2)));
    rules.put(0.0, Arrays.asList(new Pair(2, 4), new Pair(3, 3), new Pair(4, 2));
    // etc
}
andri
But to avoid warnings, all your Pairs will have to be constructed as new Pair<Integer, Integer>(x, y). In this case, I'd prefer a specially made pair class.
Michael Myers
True. I forgot to add the type parameters to the instantiations; a special pair class will indeed be better.
andri
Or you could add an `of` method, so you have `Pair.of(1, 3)` or even `of(1, 3)`.
Tom Hawtin - tackline
A: 

Can you wrap a class around Integer[][] called, say Point?

That would make you have a

HashMap<Double, List<Point>>
Nick Stinemates
A: 

I would start with a MultiValueMap. http://larvalabs.com/collections/.

This way you can do:

private static final MultiValueMap<Double, Integer[]> rules;
   static {
      MultiValueMap<Double, Integer[]> map = new MultiValueMap <Double, Integer[]>();

      map.put(-0.6, new Integer[] { 1, 3 });
      map.put(-0.3, new Integer[] { 2, 2 });
      map.put(0.0, new Integer[]  { 2, 4 }, new Integer[]{ 3, 3 }, new Integer[]{ 4, 2 } );
      map.put(0.3, new Integer[]  { 4, 4 } );
      map.put(0.6, new Integer[]  { 5, 3 } );
      rules = map;
   };

It looks also like you are aways using pairs of integers as the list of Keys. It would probably clean your interface up if you refered to that as a RulePair or some other specified object. Thus 'typing' your Integer array more specificially.

Nathan Feger
+2  A: 

Using a class for the pairs of integers should be the first. Or is this a coincidence, that all arrays containing a bunch of pairs?

The second thing is, that these initialization-data could be read from a configuration-file.

Edit: As I looked again on this code, I realized that Doubles as keys in a Map is somewhat risky. If you produce Doubles as a result of an mathematical operation, it is not clear, if they will be equal for the computer (even if they are equal in a mathematical sense). Floating-point-numbers are represented as approximation in computers. Most likely you want to associate the values with the interval (example 0.0-0.3) and not the value itself. You may avoid trouble, if you always use the same constants as keys in the array. But in this case you could use an enum as well, and no new programmer runs into trouble, if he uses his calculated doubles as keys in the map.

Mnementh
+1 for suggesting a config file
kdgregory
A: 

you could try also with a Builder; Java is not good as other languages for this kind of uses.. but here is FYI:

First shot

class RuleBuilder  {

    private Map<Double, Integer[][]> rules;

    public RuleBuilder() {
        rules = new HashMap<Double, Integer[][]>();
    }

    public RuleBuilder rule(double key, Integer[]... rows) {
        rules.put(key, rows);
        return this;
    }

    public Integer[] row(Integer... ints) {
        return ints;
    }

    public Map<Double, Integer[][]> build() {
        return rules;
    }
}

sample usage:

private static final Map<Double, Integer[][]> rules = 
                new RuleBuilder() {{
                    rule(-0.6, row(1, 3));                        
                    rule(-0.3, row(2, 2));
                    rule(0.0, row(2, 4), row(3,3), row(4, 2));
                    rule(0.3, row(4, 4));
                    rule(0.6, row(5, 3));
                }}.build();

Second shot

In order to elimate the final "build()" call and double brace init you could try with:

class RuleBuilder2 extends HashMap<Double, Integer[][]>  {

    public RuleBuilder2 rule(double key, Integer[]... rows) {
       put(key, rows);
       return this;
    }

    public Integer[] row(Integer... ints) {
        return ints;
    }
}

in this case the code is a little better:

private static final Map<Double, Integer[][]> rules2 =
                new RuleBuilder2().
                    rule(-0.6, row(1, 3)).
                    rule(-0.3, row(2, 2)).
                    rule(0.0, row(2, 4), row(3,3), row(4, 2)).
                    rule(0.3, row(4, 4)).
                    rule(0.6, row(5, 3));

EDIT

Probably the names that I've used are not so meaningful; boxed/unboxed conversion is still a problem but this is a problem of Java

dfa
I wonder if it is obvious enough (given context) that the `row` has two elements. So you could just write, `rule(0.0, 2,4, 3,3);`, with some runtime checking.
Tom Hawtin - tackline
no, it isn't obvious to me since he has used an Integer[][]; however the idea behind this code doesn't change
dfa
As result of using Double Square syntax sugar you'll get redundant anonymous class. If such syntax is overused in application then "OutOfMemoryError: PermGen Space" is something you should be aware of.
ruslan
yes, that is right; the use of double brace init can be changed easily with method chaining
dfa
A: 

There's not much you can do here. The warnings have to be suppressed; in practice, you never have to worry about serialVersionUID unless you are in fact planning to serialize this object.

The boxing can (and probably should) be removed by using a typed collection as described in other answers here. To remove the boilerplate, you'll have to use a method. For example:

private static void put (double key, int x, int y) {
  rules.put(key, new Point(x,y));
}
Paul Brinkley