« poprzedni punkt  następny punkt »

2. Zmienne tablicowe jako argumenty i wyniki metod. Użycie zmiennych tablicowych w przypisaniach
Zmiennej tablicowej typu typA[] można przypisać wartość zmiennej tablicowej typu typB[] pod warunkiem, że dopuszczalne jest przypisanie wartości typu B zmiennej typu A.
Każdej zmiennej tablicowej - jak każdej zmiennej zawierającej referencję - można przypisać wartość null

Ponieważ zmienna oznaczająca tablicę zawiera referencje do tablicy, to - pod pewnymi warunkami -  możemy jej przypisać referencję do innej tablicy.


W bardzo naturalny sposób możemy przypisywać referencje w przypadku gdy mamy do czynienia z tym samym typem tablic (a zatem - z tym samym typem elementów oraz z tą samą liczbą wymiarów).

Tak samo jak w przypadku innych obiektów - nie należy mylić przypisania zmiennych tablicowych (czyli referencji)  z kopiowaniem zawartości tablic.

Na przykład poniższy program:

public class Test {

  public static void main(String[] args) {
    byte[] b1 = {1, 2, 3 };
    byte[] b2 = {1, 2, 3, 5, 5 };
    byte[] b = b2;
    b2 = b1;
    b2[0] = 77;
    b[0] = 99;
    System.out.print("\nTablica \"b1\":");
    for (int i=0; i < b1.length; i++) System.out.print(" " + b1[i]);
    System.out.print("\nTablica \"b2\":");
    for (int i=0; i < b2.length; i++) System.out.print(" " + b2[i]);
    System.out.print("\nTablica \"b\":");
    for (int i=0; i < b.length; i++) System.out.print(" " + b[i]);
  }

}

Tablica "b1": 77 2 3
Tablica "b2": 77 2 3
Tablica "b": 99 2 3 5 5
Wyprowadzi pokazane obok wyniki.

Zwróćmy uwagę:

  • na początku b1 oznaczało tablicę {1,2,3} a b2 tablicę {1,2,3,5,5}
  • po przypisaniu b2 = b1; b2 oznacza tablicę {1, 2, 3}
  • zatem przypisanie b2[0] = 77 zmieni w tej tablicy pierwszy element
  • ale na tę samą tablicę nadal wskazuje b1, dlatego wypisanie elementów tablicy "b1" da 77 2 3
  • zmiennej b przypisano wcześniej referencję do tablicy oznaczanej przez b2 {1,2,3,5,5} - dlatego za pomocą b możemy zmienić jej pierwszy element i wypisać wszystkie: 99 2 3 5 5

W tym przykładzie przez chwilę mogło dziwić, że "na tablicę" b2 "podstawiamy tablicę" (o innych rozmiarach!) - b1. Nie przeczy to jednak zasadzie, że - po ustaleniu - rozmiary tablic nie mogą być zmieniane. No, tak - przecież przypisanie dotyczy referencji do tablic, a nie samych tablic. W przypadku referencji różne rozmiary tablic na które one wskazują nie są żadną przeszkodą przy przypisaniu - rozmiary tablic pozostają bez zmian.
I druga ważna kwestia, wymagająca jeszcze raz szczególnego podkreślenia: na jedną tablicę może wskazywać kilka zmiennych tablicowych. Za pomocą każdej z nich (i operacji indeksowania) możemy zmieniać wartości elementów tej jednej tablicy, a odwolania do tej tablicy poprzez inne zmienne będą - oczywiście - uwzględniać te zmiany (np. pokazywać zmienione wartości).

Przekazanie argumentów metodzie i zwrot jej wyniku mogą być traktowane jako szczególny rodzaj operacji przypisania.
Zatem metody mogą działać na tablicach, do których referencje otrzymują w postaci parametrów i mogą zwracać wyniki - referencje do tablic.

W przypadku tablic jednowymiarowych typem zmiennych "tablicowych" (inaczej referencji do tablic) jest T [], gdzie T - nazwa typu elementów tablicy. Zatem w nagłówku metod - parametry (które mają oznaczać tablice; są referencjami do tablic) deklarujemy właśnie za pomocą takich nazw typu. Oczywiście, gdy wywołujemy metodę - to na liście argumentów podajemy nazwy zmiennych tablicowych (już bez nawiasowych kwadratowych).
Jeśli metoda zwraca wynik - referencję do tablicy, to typem wyniku jest również odpowiedni typ tablicowy.

Np. metoda o nazwie dblVal, która zwraca referencję do nowoutworzonej tablicy liczb całkowitych, wartości elementów której są podwojonymi wartościami elementów tablicy liczb całkowitych, do której referencja przekazana została metodzie jako argument, może być zdefiniowana i użyta tak:

public class Test {

  Test() {
    int[] a = {1, 2, 3, 4 };
    int[] wynik = dblVal(a);
    for (int i=0; i < wynik.length; i++)
      System.out.print(" " + wynik[i]);
  }

  int[] dblVal(int[] tab) {
    int[] w = new int[tab.length];  // utworzenie tablicy "pod wynik"
                                    // jej rozmiary muszą być równe
                                    // rozmiarom tablicy-argumentu
    for (int i=0; i < w.length; i++) w[i] = tab[i]*2;
    return w;
  }

  public static void main(String[] args) {
    new Test();
  }

}

Dobremu zrozumieniu przekazywania argumentów tablicowych i zwracania tablicowych wyników powinien sprzyjać poniższy rysunek:

R

Proszę zwrócić uwagę jak niezręczny był opis działania metody dblVal:
"zwraca referencję do nowoutworzonej tablicy liczb całkowitych, wartości elementów której są podwojonymi wartościami elementów tablicy liczb całkowitych, do której referencja przekazana została metodzie jako argument"
Aby podkreślić sens metody dblVal powinniśmy raczej powiedzieć - nie tylko nieprecyzyjnie, ale nawet nieprawdziwie:
"Metoda dblVal zwraca tablicę, wartości elementów której są podwojonymi wartościami elementów tablicy przekazanej jako argument".

Często dla uproszczenia będziemy mówić, że metoda otrzymuje jako argument - tablicę i zwraca jako wynik - tablicę. Jest to skrót myślowy: pamiętajmy, że zawsze chodzi o referencje do tablic

Pamiętamy, że w Javie argumenty przekazywane są metodom przez wartość.
Gdy argumentem jest zmienna tablicowa - przekazywana jest referencja do tablicy - i tej oczywiście w metodzie nie jesteśmy w stanie efektywnie zmienić. Nic jednak nie stoi na przeszkodzie, by zmienić elementy przekazanej tablicy.

Obrazuje to poniższy program.

public class Test3 {

  public static void main(String[] args) {
    new Test3();
  }

  Test3() {
    int[] tab = { 2, 5, 7 };
    chgTab1(tab);
    showTab("Po wywołaniu metody chgTab1 tablica oznaczana przez tab", tab);
    chgTab2(tab);
    showTab("Po wywołaniu metody chgTab2 tablica oznaczana przez tab", tab);
  }

  void chgTab1(int[] tab) {
    int[] nowa = { 3, 6, 8 };
    tab = nowa;
    showTab("W metodzie chgTab1 tablica oznaczana przez tab", tab);
  }

  void chgTab2(int[] tab) {
    for (int i=0; i < tab.length; i++) tab[i]++;
  }

  void showTab(String s, int[] tab) {
    System.out.println(s);
    for (int i=0; i < tab.length; i++) System.out.print(" " + tab[i]);
    System.out.print('\n');
  }

}

który da następujący wynik:

W metodzie chgTab1 tablica oznaczana przez tab
 3 6 8
Po wywołaniu metody chgTab1 tablica oznaczana przez tab
 2 5 7
Po wywołaniu metody chgTab2 tablica oznaczana przez tab
 3 6 8

Pora na bardziej praktyczne ćwiczenia.
Zadanie. Napisać program-klasę, w którym dostarczane są metody operujące na tablicach liczb całkowitych:

  • sum - zwracająca sumę wszystkich elementów przekazanej tablicy
  • max - zwracająca maksymalny element przekazanej tablicy
  • min - zwracająca minimalny element w przekazanej tablicy
  • average -zwracająca średnią arytmetyczną elementów przekazanej tablicy
  • increase - zwiększająca wartości elementów przekazanej tablicy
  • decrease - zmneijszająca wartości elementów przekazanej tablicy
  • add - zwracająca tablicę, której elementy są sumą elementów dwóch przekazanych tablic

Przetestować działanie metod za pomocą dwóch tablic inicjowanych w tekście programu.


Przed lekturą dalszego tekstu proszę rozwiązać to zadanie samodzielnie


Rozwiązanie:

public class IntTab {

  public int sum(int[] a) {
    int sum = 0;
    for (int i=0; i < a.length; i++) sum += a[i];
    return sum;
  }

  public int max(int[] a) {
    int max = a[0];
    for (int i=1; i < a.length; i++)
      if (a[i] > max) max = a[i];
    return max;
  }

  public int min(int[] a) {
    int min = a[0];
    for (int i=1; i < a.length; i++)
      if (a[i] < min) min = a[i];
    return min;
  }

  public double average(int[] a) {
    double l = a.length;          // a dlaczego tak ?
    return sum(a)/l;              // - by dzielenie dało wynik typu double
  }

  public void increase(int[] a) {
    for (int i=0; i < a.length; i++) a[i]++;
  }

  public void decrease(int[] a) {
    for (int i=0; i < a.length; i++) a[i]--;
  }

  public int[] add(int[] a, int[] b) {
    if (a.length != b.length) {
       System.out.println("Rozmiary tablic muszą być takie same");
       return null;
    }
    int[] suma = new int[a.length];
    for (int i=0; i < a.length; i++) suma[i] = a[i] + b[i];
    return suma;
  }


  public static void main(String[] args) {
    new IntTab();
   }

  public IntTab() {
    int[] x = { 1, 3, 5 };
    int[] y = { 2, 4, 6 };
    showScalar("Suma x", sum(x) );
    showScalar("Suma y", sum(y) );
    showScalar("Max x", max(x) );
    showScalar("Max y", max(y) );
    showScalar("Min x", min(x) );
    showScalar("Min y", min(y) );
    showScalar("Srednia x", average(x) );
    showScalar("Srednia y", average(y) );
    increase(x);
    increase(y);
    showArr("x po zwiekszeniu", x);
    showArr("y po zwiekszeniu", y);
    decrease(x);
    decrease(y);
    showArr("x po zmniejszeniu", x);
    showArr("y po zmniejszeniu", y);
    showArr("x + y", add(x,y));
 }

 private void showScalar(String s, int v) {   // metoda przeciążona
    System.out.println(s + " = " + v);
 }

 private void showScalar(String s, double v) { // metoda przeciążona
    System.out.println(s + " = " + v);
 }

 private void showArr(String s, int[] a) {
   System.out.println(s + ":");
   for (int i=0; i < a.length; i++) System.out.print(" " + a[i]);
   System.out.print('\n');
 }

}

Wynik działania programu:

Suma x = 9
Suma y = 12
Max x = 5
Max y = 6
Min x = 1
Min y = 2
Srednia x = 3.0
Srednia y = 4.0
x po zwiekszeniu:
 2 4 6
y po zwiekszeniu:
 3 5 7
x po zmniejszeniu:
 1 3 5
y po zmniejszeniu:
 2 4 6
x + y:
 3 7 11


« poprzedni punkt  następny punkt »