简体   繁体   中英

Iteration over multidimesionn ArrayList

I searched a lot for answers to my question, but I found nothing so far.

I'm implementing a class for some operations on matrices. At the beginning I used 2x2 arrays, and they worked, but now for some other methods I've decided to switch to 2x2 ArrayList that are more versatiles.

I once found in this site a class for adding elements to these kind of lists ( link ), and just for comfort I added some new methods:

import java.util.ArrayList;

public class TwoDimensionalArrayList<T> extends ArrayList<ArrayList<T>>
{
    private static final long serialVersionUID = 1L;

    public void addToInnerArray(int index, T element)
    {
        while (index >= this.size())
        {
            this.add(new ArrayList<T>());
        }
        this.get(index).add(element);
    }

    public void addToInnerArray(int index, int index2, T element)
    {
        while (index >= this.size())
        {
            this.add(new ArrayList<T>());
        }

        ArrayList<T> inner = this.get(index);
        while (index2 >= inner.size())
        {
            inner.add(null);
        }

        inner.set(index2, element);
    }

    public T getFromInnerArray(int index, int index2)
    {
        ArrayList<T> inner = this.get(index);
        return inner.get(index2);
    }

    public void removeFromInnerArray(int index, int index2)
    {
        ArrayList<T> inner = this.get(index);
        inner.remove(index2);
        this.remove(index);
        this.add(index, inner);
    }

    public void setToInnerArray(int index, int index2, T t)
    {
        this.removeFromInnerArray(index, index2);
        this.addToInnerArray(index, index2, t);
    }

    public final String repr()
    {
        StringBuffer str = new StringBuffer();
        int i = 0;
        int j = 0;

        while(i < this.size())
        {
            while(j < this.get(i).size())
            {
                str.append(String.valueOf(this.get(i).get(j)));
                str.append(" ");
                j += 1;
            }
            j = 0;
            str.append("\n");
            i += 1;
        }

        return str.toString();
    }
}

Then my class for matrices is this:

import java.util.Scanner;
import java.util.Arrays;
import java.util.ArrayList;
import java.lang.reflect.Array;
import java.lang.Math;

import informatica.python.TwoDimensionalArrayList;

public final class Matrix
{
    private final int nrows;
    private final int ncolumns;
    public final TwoDimensionalArrayList<Double> matrix = new TwoDimensionalArrayList<Double>();
    //private final byte precision = 3;

    Matrix(int rows, int cols)
    {
        this.nrows = rows;
        this.ncolumns = cols;
        /*for(int i = 0; i < this.nrows; i++)
        {
            matrix.add(new ArrayList<Double>());
        }*/
    }

    public final void init()
    {
        Scanner sc = new Scanner(System.in);
        for(int i = 0; i < this.nrows; i++)
        {
            for(int j = 0; j < this.ncolumns; j++)
            {
                matrix.addToInnerArray(i, (Double) sc.nextDouble());
            }
        }
        //sc.close();
    }

    public final void setValue(int row, int col, Double val)
    {
        this.matrix.setToInnerArray(row, col, val);
    }

    public final void setValue(int row, ArrayList<Double> val)
    {
        this.matrix.set(row, val);
    }

    public final void setValue(ArrayList<ArrayList<Double>> val)
    {
        this.matrix.clear();
        for(ArrayList<Double> subList: val)
        {
            this.matrix.add(subList);
        }
    }

    public final Double getValue(int row, int col)
    {
        return this.matrix.getFromInnerArray(row, col);
    }

    public final ArrayList<Double> getValue(int row)
    {
        return this.matrix.get(row);
    }

    public final ArrayList<ArrayList<Double>> getValue()
    {
        return this.matrix;
    }

    /*public final int getPrecision()
    {
        return Math.pow(10,precision);
    }*/

    public final int[] getLength()
    {
        int[] len = new int[2];
        ArrayList<Double> subMatrix = this.matrix.get(0);

        len[0] = this.matrix.size();
        len[1] = subMatrix.size();

        return len;
    }

    public final String repr()
    {
        return this.matrix.repr();
    }

    public final void sum(Matrix b)
    {
        if(Arrays.equals(this.getLength(), b.getLength()))
        {
            for(int i = 0; i < this.nrows; i++)
            {
                for(int j = 0; j < this.ncolumns; j++)
                {
                    System.out.print(i + ", " + j + ": ");
                    System.out.print(this.getValue(i,j) + " + " + b.getValue(i,j) + " = ");
                    Double r = (Double) (double) Math.round((this.getValue(i,j) + b.getValue(i,j)) * 1000) / 1000;
                    System.out.println(r);
                    this.setValue(i, j, r);
                }
            }
        }
        else
        {
            System.err.println("Cannot sum two non-similar matrices.");
        }
    }
}

I've written more methods, but the logic is always the same (difference, scalar product, etc..).

This is instead the app to run:

import informatica.Matrix;
import informatica.python.TwoDimensionalArrayList;

import java.lang.reflect.Array;
import java.util.ArrayList;

class App
{
    public static void main(String[] args)
    {
        Matrix a = new Matrix(2,3);
        Matrix b = new Matrix(2,3);

        a.init();
        b.init();
        System.out.println(a.repr());
        System.out.println(b.repr());

        a.sum(b);
    }
}

I cannot sincerely find any semantic error but eclipse raises this error:

//input
1 2 3
4 5 6
//two different matrices
7 8 9
10 11 12
//end input
1.0 2.0 3.0
4.0 5.0 6.0

7.0 8.0 9.0
10.0 11.0 12.0
Exception in thread "main"
0, 0: 1.0 + 7.0 = 8.0
0, 1: 3.0 + 8.0 = 11.0
0, 2: java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
at java.util.ArrayList.rangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at informatica.python.TwoDimensionalArrayList.getFromInnerArray(TwoDimensionalArrayList.java:37)
at informatica.Matrix.getValue(Matrix.java:62)
at informatica.Matrix.sum(Matrix.java:105)
at informatica.App.main(App.java:25)

So as far as I can see, it seems that an index goes on by its own, I cannot understand why it increments by 2: 1.0 + 7.0 are both (0,0), but 3.0 + 8.0 are respectively (0,2) and (0,1). Sincerely I have no idea about why this happens.

Thanks in advance.

Because you are shrinking the list and overriding the value using index here:

public void setToInnerArray(int index, int index2, T t)
{
    this.removeFromInnerArray(index, index2);//1
    this.addToInnerArray(index, index2, t);//2
}

if i pass index = 0 and index 2 as 0 and value t as 10 and i have [3,4] in arrayList referred then above will:

  1. remove 3 from index 0 which leaves arraylist with [4] in 0th index
  2. set 10 at index 0 which means you are left with [10]

Your problem is right here:

public void addToInnerArray(int index, int index2, T element) {
    while (index >= this.size()) {
        this.add(new ArrayList<T>());
    }

    ArrayList<T> inner = this.get(index);
    while (index2 >= inner.size()) {
        inner.add(null);
    }

    inner.set(index2, element); // <- this line
}

You're removing the old element from the inner array list by using setToInnerArray and removeFromInnerArray , but instead of adding the new element, you're overwritting an existing element. Therefore the inner list remains at 2 instead of 3 elements.

Change that mentioned line to:

inner.add(index2, element); // "add" instead of "set"

You might try to get familiar with using debuggers, because they could help you in situations like this.

The problem is that you are changing the matrix during the operation. You call

a.sum(b);

which calls

for (int i = 0; i < this.nrows; i++) {
    for (int j = 0; j < this.ncolumns; j++) {
        System.out.print(i + ", " + j + ": ");
        System.out.print(this.getValue(i, j) + " + " + b.getValue(i, j) + " = ");
        Double r = (Double) (double) Math.round((this.getValue(i, j) + b.getValue(i, j)) * 1000) / 1000;
        System.out.println(r);
        this.setValue(i, j, r);
    }
}

The this in the last line this.setValue(i, j, r) refers to a (the invoking instance). What you are doing is setting the result into the matrix which calls the operation instead of setting the result to a new matrix.

What you would do better with is making sum a static method of Matrix which returns a new matrix and use

Matrix result = Matrix.sum(a, b);

Edit: As mentioned in other answers, the precise line which causes the problem is in the method addToInnerArray which is called inside this.setValue . However, when you will want to do multiplication which reuses indices, you will not get the right result anyway (exception or not) since you are changing your input on-the-fly. The design with static methods as operation ( sum , scalarProduct , vectorProd etc.) will ensure this won't be a problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM