浅笑博客
浅笑博客
Java进阶学习——泛型实例(泛型矩阵类)
Java进阶学习——泛型实例(泛型矩阵类)

引言

本文根据《Java语言程序设计 进阶篇 》(Y.Daniel Liang)进行学习总结编写。
本文章为原创性文章,如有转载请注明出处,尊重原创,谢谢。

上一节我们学习了java泛型的一些基础。这一节通过泛型类型设计一个矩阵操作类,来进一步学习和理解泛型的应用。由于对于所有的矩阵,除了元素类型不同以外,它们的一般操作都是类似的,我们可以像上一节受限的泛型类型中那个例子那样设计一个抽象的父类,其中包括矩阵共享的通用操作。上节的那个例子只是在抽象父类里设计了一个抽象方法,这一节我们直接将矩阵抽象父类设计成泛型类。

源程序

设计的 矩阵抽象泛型父类如下:

public abstract class BaseMatrix<E extends Number> {
    protected abstract E add(E o1,E o2);
    protected abstract E multiply(E o1,E o2);
    protected abstract E zero();

    public E[][] addMatrix(E[][] m1,E[][] m2){
        if(m2.length == 0 || m1.length != m2.length || m1[0].length != m2[0].length){
            throw new RuntimeException("Illegal Operation");
        }
        E[][] res = (E[][]) new Number[m1.length][m1[0].length];
        for (int i = 0; i < res.length; i++) {
            for (int j = 0; j < res[i].length; j++) {
                res[i][j] = add(m1[i][j],m2[i][j]);
            }
        }
        return res;
    }

    public E[][] multiplyMatrix(E[][] m1,E[][] m2){
        if(m1.length==0||m2.length==0||m1[0].length!=m2.length){
            throw new RuntimeException("Illegal Operation");
        }
        E[][] res = (E[][]) new Number[m1.length][m2[0].length];
        for (int i = 0; i < res.length; i++) {
            for (int j = 0; j < res[i].length; j++) {
                res[i][j] = zero();
                for (int k = 0; k < m2.length; k++) {
                    res[i][j] = add(res[i][j],multiply(m1[i][k],m2[k][j]));
                }
            }
        }
        return res;
    }
    public void print(E[][] o){
        for (int i = 0; i < o.length; i++) {
            for (int j = 0; j < o[i].length; j++) {
                System.out.print(o[i][j]+" ");
            }
            System.out.println();
        }
        System.out.println();
    }
}

addMatrix()和multiplyMatrix()分别为将泛型类型为E[][]的2个矩阵进行相加和相乘。3个抽象方法,add()、multiply()、zero()分别在其子类中实现为两个泛型类型为E的参数的和、积和返回0,用于在addMatrix()和multiplyMatrix() 中计算。

下面为实现的2个子类:IntegerMatrix(整数矩阵)和RationalMatrix(分数矩阵)。其中 RationalMatrix 类将泛型类型参数传为 Rational , Rational 为自定义的继承Number的一个分数类。

public class IntegerMatrix extends BaseMatrix<Integer> {
    @Override
    protected Integer add(Integer o1, Integer o2) {
        return o1+o2;
    }

    @Override
    protected Integer multiply(Integer o1, Integer o2) {
        return o1*o2;
    }

    @Override
    protected Integer zero() {
        return 0;
    }
}
public class RationalMatrix extends BaseMatrix<Rational> {
    @Override
    protected Rational add(Rational o1, Rational o2) {
        return o1.add(o2);
    }

    @Override
    protected Rational multiply(Rational o1, Rational o2) {
        return o1.multiply(o2);
    }

    @Override
    protected Rational zero() {
        return new Rational();
    }
}

下面为Rational 类的实现:

public class Rational extends Number implements Comparable {
    /**
     * 分子
     */
    private long numerator = 0;
    /**
     * 分母
     */
    private long denominator = 1;

    public Rational(){
        this(0,1);
    }

    public Rational(long numerator, long denominator) {
        // TODO Auto-generated constructor stub
        long gcd = gcd(numerator,denominator);
        this.numerator = ((denominator>0)?1:-1)*numerator/gcd;
        this.denominator = Math.abs(denominator)/gcd;
    }

    /**
     * 求最大公约数
     * @param n
     * @param d
     * @return
     */
    private static long gcd(long n, long d) {
        // TODO Auto-generated method stub
        long n1 = Math.abs(n);
        long n2 = Math.abs(d);
        int gcd = 1;

        for(int k = 1;k <= n1 && k <= n2;k++){
            if(n1%k==0 && n2%k==0) {
                gcd = k;
            }
        }
        return gcd;
    }

    public long getNumerator(){
        return numerator;
    }
    public long getDenominator(){
        return denominator;
    }

    public Rational add(Rational secondRational){
        long n=numerator*secondRational.getDenominator()+
                denominator*secondRational.getNumerator();
        long d=denominator*secondRational.getDenominator();
        return new Rational(n,d);
    }

    public Rational subtract(Rational secondRational){
        long n = numerator*secondRational.getDenominator()-
                denominator*secondRational.getNumerator();
        long d = denominator*secondRational.getDenominator();
        return new Rational(n,d);
    }

    public Rational multiply(Rational sR){
        long n = numerator*sR.getNumerator();
        long d = denominator*sR.getDenominator();
        return new Rational(n,d);
    }

    public Rational divide(Rational sR){
        long n = numerator*sR.denominator;
        long d = denominator*sR.numerator;
        return new Rational(n,d);
    }

    @Override
    public String toString(){
        if(denominator==1) {
            return numerator+"";
        } else {
            return numerator+"/"+denominator;
        }
    }

    @Override
    public boolean equals(Object parm1){
        return (this.subtract((Rational) (parm1))).getNumerator() == 0;
    }



    @Override
    public int compareTo(Object o) {
        // TODO Auto-generated method stub
        if((this.subtract((Rational)o)).getNumerator()>0)
            return 1;
        else if((this.subtract((Rational)o)).getNumerator()<0)
            return -1;
        else
            return 0;
    }

    @Override
    public int intValue() {
        // TODO Auto-generated method stub
        return (int)doubleValue();
    }

    @Override
    public long longValue() {
        // TODO Auto-generated method stub
        return (long)doubleValue();
    }

    @Override
    public float floatValue() {
        // TODO Auto-generated method stub
        return (float)doubleValue();
    }

    @Override
    public double doubleValue() {
        // TODO Auto-generated method stub
        return numerator*1.0/denominator;
    }
}

代码都比较好理解,就不再一一说明了。

public class Test {
    public static void main(String[] args) {
        IntegerMatrix integerMatrix = new IntegerMatrix();
        Integer[][] im1 = new Integer[][]{{1,2,3},{4,5,6},{1,1,1,}};
        Integer[][] im2 = new Integer[][]{{1,1,1},{2,2,2,},{0,0,0}};
        integerMatrix.print(integerMatrix.addMatrix(im1,im2));

        RationalMatrix rationalMatrix = new RationalMatrix();
        Rational[][] rm1 = new Rational[2][3];
        Rational[][] rm2 = new Rational[3][2];
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if(i<2){
                    rm1[i][j] = new Rational(i+1,j+5);
                }
                if(j<2){
                    rm2[i][j] = new Rational(i+1,j+6);
                }
            }
        }
        rationalMatrix.print(rm1);
        rationalMatrix.print(rm2);
        rationalMatrix.print(rationalMatrix.multiplyMatrix(rm1,rm2));
    }
}

输出:

2 3 4 
6 7 8 
1 1 1 

1/5 1/6 1/7 
2/5 1/3 2/7 

1/6 1/7 
1/3 2/7 
1/2 3/7 

101/630 101/735 
101/315 202/735 

改进

上面的是书上的一个例子。看到这里,大家也许会觉得这个类用起来不是那么好。大家也许回想为什么不把矩阵直接封装在类里呢?其实呢,也是可以将泛型类型E[][]封装在类中,但大家别忘了,泛型的使用限制,即不能使用泛型类型参数来创建数组,即不能使用new E[],同理也不能使用new E[][]。既然都不能new,那怎么初始化封装的矩阵呢?我们可以使用构造函数直接传(见下面代码),当然也可以使用new Object[][]实例化后再类型强制转化(下面例子中multiply方法中有用的)。这是我自己写的一个矩阵操作类,直接使用泛型参数类型E,元素的操作封装在一个泛型接口中。

public class GenericMatrix<E> {
    private E[][] matrix;
    private OperationInterface<E> operationInterface;

    //为了方便 构造时直接传入矩阵
    public GenericMatrix(E[][] matrix) {
        this.matrix = matrix;
    }

    /**
     * 设置接口 指定元素具体操作方法如何进行
     * @param operationInterface
     * @return
     */
    public GenericMatrix<E> setOperationInterface(OperationInterface<E> operationInterface){
        this.operationInterface = operationInterface;
        return this;
    }

    public int getRow(){
        return matrix.length;
    }

    public int getColumn(){
        return matrix[0].length;
    }

    public E getValue(int i,int j){
        return matrix[i][j];
    }

    public GenericMatrix<E> add(GenericMatrix<E> o2){
        if(!canAdd(o2)){
            throw new RuntimeException("不能相加");
        }
        for (int i = 0; i < getRow(); i++) {
            for (int j = 0; j < getColumn(); j++) {
                matrix[i][j] = operationInterface.add(matrix[i][j],o2.getValue(i,j));
            }
        }
        return this;
    }

    private boolean canAdd(GenericMatrix<E> o2){
        if(getColumn()==0||getRow()==0||getRow()!=o2.getRow()||getColumn()!=o2.getColumn()){
            return false;
        }
        return true;
    }

    public GenericMatrix<E> multiply(GenericMatrix<E> o2){
        if(!canMultiply(o2)){
            throw new RuntimeException("不能相乘");
        }
        //可以使用下面的方法实例化一个泛型类型参数E的矩阵
        E[][] res = (E[][])new Object[getRow()][o2.getColumn()];
        for (int i = 0; i < res.length; i++) {
            for (int j = 0; j < res[i].length; j++) {
                E temp = operationInterface.zero();
                for (int k = 0; k < o2.getRow(); k++) {
                    temp = operationInterface.add(temp,operationInterface.multiply(getValue(i,k),o2.getValue(k,j)));
                }
                res[i][j] = temp;
            }
        }
        return new GenericMatrix<E>(res);
    }

    private boolean canMultiply(GenericMatrix<E> o2){
        if(getRow()==0||o2.getRow()==0||getColumn()!=o2.getRow()){
            return false;
        }
        return true;
    }

    public void print(){
        for (int i = 0; i < getRow(); i++) {
            for (int j = 0; j < getColumn(); j++) {
                System.out.print(matrix[i][j]+" ");
            }
            System.out.println();
        }
        System.out.println();
    }
}

代码也比较清楚,就不一一解释了。运行测试:

public class Test {
    public static void main(String[] args) {
        GenericMatrix<Integer> igenericMatrix = new GenericMatrix<>(new Integer[][]{{1,2,3},{4,5,6},{1,1,1}});
        GenericMatrix<Integer> igenericMatrix2 = new GenericMatrix<>(new Integer[][]{{1,1,1},{2,2,2,},{0,0,0}});
        igenericMatrix.setOperationInterface(new OperationInterface<Integer>() {
            @Override
            public Integer add(Integer o1, Integer o2) {
                return o1+o2;
            }

            @Override
            public Integer multiply(Integer o1, Integer o2) {
                return o1*o2;
            }

            @Override
            public Integer zero() {
                return 0;
            }
        }).add(igenericMatrix2).print();
        igenericMatrix.multiply(igenericMatrix2).print();

        //先初始化2个Rational矩阵
        Rational[][] rm1 = new Rational[2][3];
        Rational[][] rm2 = new Rational[3][2];
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if(i<2){
                    rm1[i][j] = new Rational(i+1,j+5);
                }
                if(j<2){
                    rm2[i][j] = new Rational(i+1,j+6);
                }
            }
        }
        GenericMatrix<Rational> mgenericMatrix = new GenericMatrix<>(rm1);
        GenericMatrix<Rational> mgenericMatrix2 = new GenericMatrix<>(rm2);
        mgenericMatrix.setOperationInterface(new OperationInterface<Rational>() {
            @Override
            public Rational add(Rational o1, Rational o2) {
                return o1.add(o2);
            }

            @Override
            public Rational multiply(Rational o1, Rational o2) {
                return o1.multiply(o2);
            }

            @Override
            public Rational zero() {
                return new Rational();
            }
        }).multiply(mgenericMatrix2).print();
    }
}

输出:

2 3 4 
6 7 8 
1 1 1 

8 8 8 
20 20 20 
3 3 3 

101/630 101/735 
101/315 202/735 

在上面的例子中可以看到,为了方便我初始化内部的矩阵时是用构造方法传入的,矩阵相加的add方法由于不用新建矩阵,直接基于原矩阵进行相加。但在矩阵相乘的multiply方法中由于矩阵乘法的限制,就不能在原来的基础上进行操作了,必须重新new一个结果大小的矩阵使用,此时只能使用new Object[][]后再类型强制转换的方法去实现了。

结语

通过这个例子,主要掌握泛型在实际开发中的应用,同时能够掌握泛型类与泛型接口的一些使用方法。如文中有任何错误或待改进之处,欢迎评论交流。

本文章为原创性文章,如有转载请注明出处,尊重原创,谢谢。

发表评论

textsms
account_circle
email

浅笑博客

Java进阶学习——泛型实例(泛型矩阵类)
引言 本文根据《Java语言程序设计 进阶篇 》(Y.Daniel Liang)进行学习总结编写。 本文章为原创性文章,如有转载请注明出处,尊重原创,谢谢。 上一节我们学习了java泛型的一些基…
扫描二维码继续阅读
2020-04-03