10

Consider a situation where a class implements the same basic behavior, methods, et cetera, but multiple different versions of that class could exist for different uses. In my particular case, I have a vector (a geometric vector, not a list) and that vector could apply to any N-dimensional Euclidean space (1 dimensional, 2 dimensional, ...). How can this class / type be defined?

This would be easy in C++ where class templates can have actual values as parameters, but we don't have that luxury in Java.

The two approaches I can think of that could be taken to solve this problem are:

  1. Having an implementation of each possible case at compile time.

    public interface Vector {
        public double magnitude();
    }
    
    public class Vector1 implements Vector {
        public final double x;
        public Vector1(double x) {
            this.x = x;
        }
        @Override
        public double magnitude() {
            return x;
        }
        public double getX() {
            return x;
        }
    }
    
    public class Vector2 implements Vector {
        public final double x, y;
        public Vector2(double x, double y) {
            this.x = x;
            this.y = y;
        }
        @Override
        public double magnitude() {
            return Math.sqrt(x * x + y * y);
        }
        public double getX() {
            return x;
        }
        public double getY() {
            return y;
        }
    }
    

    This solution is obviously very time consuming and extremely tedious to code. In this example it doesn't seem too bad, but in my actual code I'm dealing with vectors that have multiple implementations each, with up to four dimensions (x, y, z, and w). I currently have over 2,000 lines of code, even though each vector only really needs 500.

  2. Specifying parameters at runtime.

    public class Vector {
        private final double[] components;
        public Vector(double[] components) {
            this.components = components;
        }
        public int dimensions() {
            return components.length;
        }
        public double magnitude() {
            double sum = 0;
            for (double component : components) {
                sum += component * component;
            }
            return Math.sqrt(sum);
        }
        public double getComponent(int index) {
            return components[index];
        }
    }
    

    Unfortunately this solution hurts code performance, results in messier code than the former solution, and is not as safe at compile-time (it can't be guaranteed at compile-time that the vector you're dealing with actually is 2-dimensional, for example).

I am currently actually developing in Xtend, so if any Xtend solutions are available, they would also be acceptable.

7
  • Since you're using Xtend, are you doing this within the context of an Xtext DSL?
    – Dan1701
    Commented Feb 15, 2016 at 23:30
  • 2
    DSLs are great for code-gen applications. In a nutshell, you create a little language grammar, an instance of that language (describing various vectors, in this case), and some code that executes when the instance is saved (generating your Java code). There's plenty of resources and examples on the Xtext site.
    – Dan1701
    Commented Feb 16, 2016 at 1:08
  • 2
    There's a perfect solution to this problem using dependent types (it's more or less what they were created for), but alas that's not available in Java. I would go with the first solution if you only have a small, fixed number of classes (say you only use 1-, 2-, and 3-dimensional vectors), and the latter solution for more than that. Obviously I can't say for certain without running your code, but I don't think there will be the performance impact you're worried about
    – gardenhead
    Commented Oct 13, 2016 at 21:58
  • 1
    Those two classes do not have the same interface, they are not polymorphic but you are trying to use them polymorphically. Commented Oct 17, 2017 at 13:44
  • 1
    If you're writing linear algebra maths and are concerned about performance then why java. I can't see anything but problems in that.
    – Sopel
    Commented Nov 21, 2017 at 13:30

5 Answers 5

1

In cases like this, I use code generation.

I write a java application that generates the actual code. That way you can easily use a for loop to generate a bunch of different versions. I use JavaPoet, which makes it pretty straightforward to build up the actual code. Then you can integrate running the code generation into your build system.

0

I have a very similar model on my application and our solution was to simply keep a map of a dynamic size, similar to your solution 2.

You simply aren't going to need to worry about performance with a java array primative like that. We generate matrixes with upper-bound sizes of 100 columns (read: 100 dimensional vectors) by 10,000 rows, and we have had good performance with much more complex vector types than your solution 2. You might try sealing the class or marking methods as final to speed it up, but I think you're optimizing prematurely.

You can get some code-savings (at the cost of performance) by creating a base class to share your code:

public interface Vector(){

    abstract class Abstract {           
        protected abstract double[] asArray();

        int dimensions(){ return asArray().length; }

        double magnitude(){ 
            double sum = 0;
            for (double component : asArray()) {
                sum += component * component;
            }
            return Math.sqrt(sum);
        }     

        //any additional behavior here   
    }
}

public class Scalar extends Vector.Abstract {
    private double x;

    public double getX(){
        return x;
    }

    @Override
    public double[] asArray(){
        return new double[]{x};
    }
}

public class Cartesian extends Vector.Abstract {

    public double x, y;

    public double getX(){ return x; }
    public double getY(){ return y; }

    @Override public double[] asArray(){ return new double[]{x, y}; }
}

Then of course, if you're on Java-8+, you can use defaulted interfaces to make this even tighter:

public interface Vector{

    default public double magnitude(){
        double sum = 0;
        for (double component : asArray()) {
            sum += component * component;
        }
        return Math.sqrt(sum);
    }

    default public int dimensions(){
        return asArray().length;
    }

    default double getComponent(int index){
        return asArray()[index];
    }

    double[] asArray();

    // giving up a little bit of static-safety in exchange for 
    // runtime exceptions, we can implement the getX(), getY() 
    // etc methods here, 
    // and simply have them throw if the dimensionality is too low 
    // (you can of course do this on the abstract-class strategy as well)

    //document or use checked-exceptions to indicate that these methods throw IndexOutOfBounds exceptions (or a wrapped version)

    default public getX(){
        return getComponent(0);
    }
    default public getY(){
        return getComponent(1);
    }
    //...


    }

    //as a general rule, defaulted interfaces should assume statelessness, 
    // so you want to avoid putting mutating operations 
    // as defaulted methods on an interface, since they'll only make your life harder
}

Ultimately beyond that you're out of options with the JVM. You can of course write them in C++ and use something like JNA to bridge them in --this is our solution for some of the fast matrix operations, where we use fortran and intel's MKL-- but this is only going to slow things down if you simply write your matrix in C++ and call its getters/setters from java.

3
  • My main concern isn't performance, it's compile-time checking. I would really like a solution where the size of the vector and the operations that can be performed on it are determined at compile-time (like with C++ templates). Perhaps your solution is best if you're dealing with matrices that could be up to 1000 components in size, but in this case I'm only dealing with vectors with a size of 1 - 10. Commented Feb 16, 2016 at 1:11
  • If you use something like the first or second solution, you can create those subclasses. Now I am also just reading on Xtend, and it seems a fair bit like Kotlin. With Kotlin, you can probably use the data class objects to easily create 10 vector subclasses. With java, assuming you can pull all of your functionality into the base class, each subclass will take 1-10 lines. Why not create a base class?
    – Groostav
    Commented Feb 16, 2016 at 1:23
  • The example I provided is oversimplified, my actual code has a lot of methods defined for Vector such as the vector dot product, component-wise addition and multiplication, et cetera. Although I could implement these using a base class and your asArray method, those various methods wouldn't be checked at compile time (you could perform a dot-product between a scalar and a cartesian vector and it would compile fine, but fail at runtime). Commented Feb 16, 2016 at 1:58
0

Consider an enum with each named Vector having a constructor that consists of an array (initialized in the parameter list with the dimension names or similar, or perhaps just an integer for the size or an empty components array - your design), and a lambda for the getMagnitude method. You could have the enum also implement an interface for setComponents/getComponent(s), and just establish which component was which in its usage, eliminating getX, et al. You would need to initialize each object with its actual component values before use, possibly checking that the input array size matches the dimension names or size.

Then if you extend the solution to another dimension, you just modify the enum and lambda.

1
  • 1
    Please, provide a short code snippet illustration your solution. Commented Sep 13, 2016 at 21:28
0

Based on your option 2, why not simply do this? If you want to prevent use of the raw base you can make it abstract:

class Vector2 extends Vector
{
  public Vector2(double x, double y) {
    super(new double[]{x,y});
  }

  public double getX() {
    return getComponent(0);
  }

  public double getY() {
    return getComponent(1);
  }
}
4
  • This is similar to "method 2" in my question. Your solution does, however, give a way of guaranteeing type safety at compile-time, however the overhead of creating a double[] is undesirable compared to an implementation that simply uses 2 primitive doubles. In an example as minimal as this it seems like a microoptimization, but consider a much more complex case where much more metadata is involved and the type in question has a short lifetime. Commented Jul 15, 2016 at 20:18
  • 1
    Right, as it says, this is based on method 2. Based on your discussion with Groostav with regard to his answer, I got the impression that your concern was not with performance. Have you quantified this overhead i.e. creating 2 objects instead of 1? As for short life-spans, modern JVMs are optimized for this case and should have lower GC cost (basically 0) than longer lived objects. I'm not sure how the metadata plays into this. Is this metadata scalar or dimensional?
    – JimmyJames
    Commented Jul 15, 2016 at 20:35
  • The actual project I was working on was a geometry framework to be used in a hyperdimensional renderer. This means I was creating a lot more complex objects than vectors such as ellipsoids, orthotopes et cetera and transformations usually involved matrices. The complexity of working with higher dimensional geometry made type-safety for matrix and vector size desirable while there was still significant desire to avoid the creation of objects as much as possible. Commented Jul 15, 2016 at 21:18
  • What I think I was really looking for was a more automated solution that produced bytecode similar to method 1, which isn't really possible in standard Java or Xtend. When I ended up doing was using method 2 where the size parameters of these objects needed to be dynamic at runtime, and tediously creating more efficient, specialized implementations for cases where these parameters were static. The implementation would replace the "dynamic" supertype Vector with a more specialized implementation (eg Vector3) if its lifetime was to be relatively long. Commented Jul 15, 2016 at 21:22
0

One idea:

  1. An abstract base class Vector providing variable-dimension implementations based on a getComponent(i) method.
  2. Indiviual subclasses Vector1, Vector2, Vector3, covering the typical cases, overriding the Vector methods.
  3. A DynVector subclass for the general case.
  4. Factory methods with fixed-length argument lists for the typical cases, declared to return Vector1, Vector2 or Vector3.
  5. A var-args factory method, declared to return Vector, instantiating Vector1, Vector2, Vector3, or DynVector, depending on the arglist length.

This gives you good performance in typical cases and some compile-time safety (can still be improved) without sacrificing the general case.

Code skeleton:

public abstract class Vector {
    protected abstract int dimension();
    protected abstract double getComponent(int i);
    protected abstract void setComponent(int i, double value);

    public double magnitude() {
        double sum = 0.0;
        for (int i=0; i<dimension(); i++) {
            sum += getComponent(i) * getComponent(i);
        }
        return Math.sqrt(sum);
    }

    public void add(Vector other) {
        for (int i=0; i<dimension(); i++) {
            setComponent(i, getComponent(i) + other.getComponent(i));
        }
    }

    public static Vector1 create(double x) {
        return new Vector1(x);
    }

    public static Vector create(double... values) {
        switch(values.length) {
        case 1:
            return new Vector1(values[0]);
        default:
            return new DynVector(values);
        }

    }
}

class Vector1 extends Vector {
    private double x;

    public Vector1(double x) {
        super();
        this.x = x;
    }

    @Override
    public double magnitude() {
        return Math.abs(x);
    }

    @Override
    protected int dimension() {
        return 1;
    }

    @Override
    protected double getComponent(int i) {
        return x;
    }

    @Override
    protected void setComponent(int i, double value) {
        x = value;
    }

    @Override
    public void add(Vector other) {
        x += ((Vector1) other).x;
    }

    public void add(Vector1 other) {
        x += other.x;
    }
}

class DynVector extends Vector {
    private double[] values;
    public DynVector(double[] values) {
        this.values = values;
    }

    @Override
    protected int dimension() {
        return values.length;
    }

    @Override
    protected double getComponent(int i) {
        return values[i];
    }

    @Override
    protected void setComponent(int i, double value) {
        values[i] = value;
    }

}

Not the answer you're looking for? Browse other questions tagged or ask your own question.