Zanim przejdziemy do tablic wielowymiarowych pokażę parę sztuczek związanych z tablicami.
Ta strona jest częścią materiałów do kursu “Programowanie w Javie z elementami bioinformatyki dla poczatkujących”. Pozostałe materiały znajdziesz tutaj
Kopiowanie tablic
Próba skopiowania tablicy przy pomocy zwykłego operatora przypisania =
przynosi efekty nieco inne od oczekiwanych. Jeśli uruchomimy kod:
int[] tablicaOryginalna = {1, 2, 3, 4};
int[] tablicaSkopiowana = tablicaOryginalna;
System.out.println("Oryginalna, 4 element: " + tablicaOryginalna[3]);
System.out.println("Kopia, 4 element: " + tablicaSkopiowana[3]);
tablicaSkopiowana[3] = 10;
System.out.println("Oryginalna, 4 element: " + tablicaOryginalna[3]);
System.out.println("Kopia, 4 element: " + tablicaSkopiowana[3]);
to otrzymamy dość dziwny rezultat:
Oryginalna, 4 element: 4
Kopia, 4 element: 4
Oryginalna, 4 element: 10
Kopia, 4 element: 10
Jak widać, zmiana wartości przeprowadzona na kopii tablicy, spowodowała także zmianę w tablicy oryginalnej. Dlaczego tak się dzieje? Przypominam, że tablice przechowują jedynie odnośniki do elementów, skopiowanie za pomocą operatora =
tablicy powoduje, że kopia przechowuje adresy do tych samych elementów co oryginał. Skoro obie tablice wskazują na ten sam element nie ma znaczenia na której przeprowadzimy operacje, efekty będą widoczne na obu.
Zwykle jednak kopiujemy tablice po to, żeby na jednej z nich przeprowadzać pewne operacje związane ze zmianą przechowywanych przez nie wartości, a druga powinna przechować pierwotną zawartość bądź zmieniać ją w inny sposób. Jak zatem otrzymać rzeczywistą kopię tablicy?
Kopiowane po kolei elementów
Pierwszą metodą jest stworzenie tablicy o takiej samej długości (i typie rzecz jasna) co tablica, którą chcemy skopiować a następnie kopiowanie po kolei wszystkich elementów z jednej tablicy do drugiej:
1 2 3 4 5 6 7 8 9 10 11 12 |
int[] oryginal = {1, 2, 3, 4}; int[] kopia = new int[oryginal.length]; for (int i = 0; i < oryginal.length; i++) { kopia[i] = oryginal[i]; } System.out.println("Oryginalna, 4 element: " + oryginal[3]); System.out.println("Kopia, 4 element: " + kopia[3]); kopia[3] = 10; System.out.println("Oryginalna, 4 element: " + oryginal[3]); System.out.println("Kopia, 4 element: " + kopia[3]); |
Teraz wynik wygląda lepiej.
Klonowanie metodą… clone()
Tablice, które są obiektami, udostępniają pewne metody a jedną z nich jest clone()
. Bez wątpienia jest to sposób prostszy niż poprzedni:
int[] kopia = (int[])oryginal.clone();
Zauważ, że zastosowaliśmy tu rzutowanie. Bez tego też kod by zadziałał, ale stosowanie takiej praktyki pozwala uchronić się przed pewnego rodzaju błędami.
Wykorzystanie java.util.Arrays
Klasa java.util.Arrays udostępnia zestaw pożytecznych metod do obsługi tablic. Zanim przejdziemy do kopiowania tablic poznamy metodę, która pozwala w łatwy sposób wyświetlić wartości przechowywane przez tablicę:
Arrays.toString(tablica)
Na przykład po wykonaniu kodu:
int[] liczby = {1, 3, 4, 5, 8};
System.out.println(Arrays.toString(liczby));
Otrzymamy:
[1, 3, 4, 5, 8]
Teraz czas na kopiowanie:
Arrays.copyOf(tablica, długośćKopii)
Na przykład:
int[] liczby = {1, 3, 4, 5, 8};
System.out.println(Arrays.toString(liczby));
int[] kopiaLiczb = Arrays.copyOf(liczby, liczby.length);
System.out.println("Oryginał: "+ Arrays.toString(liczby));
System.out.println("Kopia: "+ Arrays.toString(kopiaLiczb));
kopiaLiczb[2]=0;
System.out.println("Oryginał: "+ Arrays.toString(liczby));
System.out.println("Kopia: "+ Arrays.toString(kopiaLiczb));
Rezultat:
Oryginał: [1, 3, 4, 5, 8]
Kopia: [1, 3, 4, 5, 8]
Oryginał: [1, 3, 4, 5, 8]
Kopia: [1, 3, 0, 5, 8]
Jak wynika z powyższego, można też skopiować część tablicy:
int[] czescLiczb = Arrays.copyOf(liczby, 3);
System.out.println("Oryginał: "+ Arrays.toString(liczby));
System.out.println("Kopia: "+ Arrays.toString(czescLiczb));
Po uruchomieniu kodu otrzymamy:
Oryginał: [1, 3, 4, 5, 8]
Kopia: [1, 3, 4]
Uwaga, jeśli tablica do której kopiujemy jest dłuższa niż liczba elementów kopiowanych, to zostanie ona skrócona, jeśli przekażemy jako argument długość tablicy kopiowanej:
1 2 3 4 5 6 7 |
int[] tab1 = new int[10]; System.out.println("tab1: " + Arrays.toString(tab1)); System.out.println("Długość tab1: " + tab1.length); int[] tab2 = {1, 2, 3, 4}; tab1 = Arrays.copyOf(tab2, tab2.length); System.out.println("tab1: " + Arrays.toString(tab1)); System.out.println("Długość tab1: " + tab1.length); |
Rezultat:
tab1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Długośc tab1: 10
tab1: [1, 2, 3, 4]
Długośc tab1: 4
Jak widać tablica tab1
została skrócona do czterech elementów. Aby tego uniknąć, wystarczy podać jako argument długość tablicy dłuższej:
tab1 = Arrays.copyOf(tab2, tab1.length);
Teraz wynik będzie taki:
tab1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Długość tab1: 10
tab1: [1, 2, 3, 4, 0, 0, 0, 0, 0, 0]
Długość tab1: 10
Poniżej opiszę jeszcze kilka użytecznych metod udostępnianych przez klasę java.util.Arrays
Metoda System.arraycopy()
Ten sposób pozwala na częściowe skopiowanie tablic. Format polecenia wygląda tak:
System.arraycopy(tablica1, indexStart1, tablica2, indexStart2, liczbaElementow)
Jak nietrudno się domyśleć zawartość tablicy1
jest począwszy od indeksu index1
kopiowania do tablicy2
, przy czym elementy są umieszczane począwszy od indeksu indexStart2
, kopiowana jest liczba elementów równa wartości liczbaElementow
.
Kilka innych możliwości klasy java.util.Arrays
Klasa java.util.Arrays
, poza wspomnianą wcześniej możliwością łatwego kopiowania tablic(copyOf()
) udostępnia inne, użyteczne metody. Poniżej wymieniłem niektóre z nich.
Arrays.toString
– wydrukowanie zawartości tablicy, zwracany jest łańcuch znakówArrays.copyOf(tablica, długośćKopii)
– kopiowanie (patrz wyżej)Arrays.fill(tablica, element)
– wypełnianie tablicy podanym elementemArrays.equals(tablica1, tablica2)
– porównanie tablic, zwracana jest wartośćfalse
lubtrue
Arrays.sort(tablica)
– sortowana jest zawartość tablicy, nic nie jest zwracane, zmieniana jest kolejność elementów w tablicy. Jeśli chcemy mieć posortowaną kopię, najpierw trzeba tablicę skopiować a później posortować kopięArrays.binarySearch(tablica, szukanyElement)
– szukanie w posortowanej tablicy, zwracany jest indeks, w którym znajduje się szukany element
Zastosowanie powyższych metod można pokazać na przykładzie:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import java.util.Arrays; public class TabliceArrays { public static void main(String[] args) { int[] liczby = {1, 5, 3, 6, 2, 1}; // Wydruk zawartości tablicy System.out.println("Zawartość: "+ Arrays.toString(liczby)); // Kopiowanie int[] kopia = Arrays.copyOf(liczby, liczby.length); // Wypełnianie int[] jedynki = new int[6]; Arrays.fill(jedynki, 1); System.out.println("Wypełnianie: "+ Arrays.toString(jedynki)); // Porównanie System.out.println("Porównanie: "+ Arrays.equals(liczby, kopia)); System.out.println("Porównanie: "+ Arrays.equals(liczby, jedynki)); // Sortowanie Arrays.sort(kopia); System.out.println("Sortowanie: "+ Arrays.toString(kopia)); // Sortowanie łańcuchów znaków String[] imiona = {"Ola", "Ala", "ala", "Ula", "Aga", "Iga"}; Arrays.sort(imiona); System.out.println("Sortowanie: "+ Arrays.toString(imiona)); // Szukanie System.out.println("Szukanie 6: "+ Arrays.binarySearch(kopia, 6)); } } |
Zauważ, że w powyższych kodach odwoływaliśmy się bezpośrednio do klasy Arrays
, nie tworzyliśmy obiektu jak na przykład w przypadku java.util.Scanner
. Jest tak, ponieważ klasa java.util.Arrays
udostępnia metody statyczne. Na razie niewiele to wyjaśnia, ale wrócimy jeszcze to tego tematu.
Pełny wykaz dostępnych metod można znaleźć w oficjalnej dokumentacji klasy pod adresem: https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html. Przy okazji warto zwrócić uwagę na to, jakie typy danych metody obsługują oraz które z nich pozwalają na pracę z tablicami wielowymiarowymi, którym poświęcę następną lekcję.
Czy można zmienić długość tablicy?
Dotychczas poznane właściwości tablic wskazują na to, że jeśli raz określimy wielkość tablicy, to nie można już jej zmienić. W zasadzie jest to prawda, ale istnieją pewne triki, które umożliwiają obejście tego ograniczenia. Powyżej, przy okazji kopiowania tablicy za pomocą Arrays.copyOf()
zmniejszyliśmy tablicę, teraz spróbujemy ją zwiększyć. Pokażę to na przykładzie. Warto jednak zauważyć, że jeśli chcemy mieć możliwość przechowywania serii elementów w strukturze o zmiennej długości to warto zainteresować się listami tablicowymi (ang. ArrayLists) o których oczywiście także napiszę w przyszłości.
Wyobraźmy sobie, że piszemy program, w którym będzie przechowywana sekwencja nukleotydów w postaci tablicy zmiennych typu char
(w końcu do czegoś się przydadzą). Na początku została ona ustalona, poprzez wpisanie pewnej początkowej sekwencji, na 6 znaków. Potem jednak chcemy przedłużyć sekwencje o trzy dodatkowe nukleotydy. Jak to zrobić?
Sposób 1
Na początku tego wpisu pokazałem, jak nie kopiować tablic, tłumacząc, że wykorzystanie operatora =
kopiuje tylko wskaźniki do elementów z jednej tablicy do drugiej, w efekcie obie tablice wskazują na te same elementy. Teraz wykorzystamy tą możliwość. Przepisz, wykonaj a potem przeanalizujmy poniższy kod.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// tworzymy tablice znaków przechowującą kolejne nukleotydy w sekwencji // początkowo przechowuje 6 znaków (nukleotydów) char[] seq = {'A','C','T','A','A','G'}; // drukowanie zawartości tablicy System.out.println("seq: " + Arrays.toString(seq)); // tworzymy tablicę tymczasową, o długości o 3 dłuższą niż tablica seq char[] tmp = new char[seq.length + 3]; System.out.println("tpm: " + Arrays.toString(tmp)); // kopiujemy zawartość tablicy seq to tmp System.arraycopy(seq, 0, tmp, 0, seq.length); System.out.println("tmp: " + Arrays.toString(tmp)); // teraz tablica seq będzie wskazywała na elementy // przechowywane w tmp a tmp ma długość 9 seq = tmp; System.out.println("seq: " + Arrays.toString(seq)); // wypełniamy puste miejsca seq[6] = 'C'; seq[7] = 'A'; seq[8] = 'T'; System.out.println("seq: " + Arrays.toString(seq)); |
Rezultat wygląda tak, przy czym niewypełnione komórki mogą być wydrukowane np. jako kwadraty:
seq: [A, C, T, A, A, G]
tpm: [ , , , , , , , , ]
tmp: [A, C, T, A, A, G, , , ]
seq: [A, C, T, A, A, G, , , ]
seq: [A, C, T, A, A, G, C, A, T]
Kluczowe w tym programie było wykonanie operacji: seq = tmp
. Od tego miejsca tablica seq
wskazuje na te same elementy, które wskazuje tablica tmp
, a ponieważ jest ich 9, to wygląda to tak, jakby z tablicy pojawiło się więcej miejsca.
Sposób 2
Drugi sposób wykorzystuje metodę Arrays.copyOf()
, którą omawialiśmy powyżej. Być może, kiedy pisałem o tym, że jeśli liczba, podana jako argument jest mniejsza niż długość tablicy do której odbywa się kopiowanie, to jest ona zmniejszana, przyszło Ci na myśl, że może to też działać w drugą stronę. Tak, działa. Poniżej znajduje się nieco zmodyfikowana wersja przykładu ze „Sposobu 1”, jest ona przy tym nieco krótsza, nie zawiera też tymczasowej tablicy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// tworzymy tablice znaków przechowującą kolejne nukleotydy w dekwencji // początkowo przechowuje 6 znaków (nukleotydów) char[] seq = {'A','C','T','A','A','G'}; // drukowanie zawartości tablicy System.out.println("seq: " + Arrays.toString(seq)); // wykorzystujemy metodę Arrays.copyOf() podając jako argument // liczbę o 3 dłuższą niż długość tablicy seq = Arrays.copyOf(seq, seq.length+3); System.out.println("seq: " + Arrays.toString(seq)); // wypełniamy puste miejsca seq[6] = 'C'; seq[7] = 'A'; seq[8] = 'T'; System.out.println("seq: " + Arrays.toString(seq)); |
Komenda seq = Arrays.copyOf(seq, seq.length+3)
wykonuje operację kopiowania do samej siebie ale ponieważ argument określający długość kopii jest o 3 większy niż tablica, po kopiowaniu tablica seq
posiada o trzy elementy więcej niż poprzednio.
Ćwiczenie – sortowanie tablicy
Znanych jest wiele algorytmów pozwalających posortować elementy w tablicy. W praktyce można oczywiście użyć klasy java.util.Arrays
ale ten sposób nie przybliży nas do zrozumienia jak taki proces może przebiegać. Poniżej pokazałem dwa, należące do najprostszych, algorytmów ale zanim przejdziesz dalej spróbuj samodzielnie wymyślić w jaki sposób takie sortowanie mogłoby przebiegać i napisać odpowiedni kod.
Podpowiedzi - kliknij aby zobaczyć
Rozwiązanie: sortowanie przez wybieranie
<
p class=”uwaga”>Ta strona jest częścią materiałów do kursu “Programowanie w Javie z elementami bioinformatyki dla poczatkujących”. Pozostałe materiały znajdziesz tutaj
Polecam obejrzeć tańce obrazujące sortowanie:
-Bubble sort: https://www.youtube.com/watch?v=lyZQPjUT5B4
-Select sort: https://www.youtube.com/watch?v=Ns4TPTC8whw
:)
Świetne! Dziękuję.
BŁEDY W KODZIE !!
int[] tablicaOryginalna = {1, 2, 3, 4};
int[] tablicaSkopiowana = tablicaOryginalna;
System.out.println(„Oryginalna, 4 element: ” + tablicaOryginalna[3]);
System.out.println(„Kopia, 4 element: ” + tablicaOryginalna[3]); // BŁAD – powinno byc tablicaSkopiowana[3]
tablicaSkopiowana[3] = 10;
System.out.println(„Oryginalna, 4 element: ” + tablicaOryginalna[3]);
System.out.println(„Kopia, 4 element: ” + tablicaOryginalna[3]); // BŁAD – powinno byc tablicaSkopiowana[3]
Rzeczywiście!. Dziękuje za zwrócenie uwagi.
Pozdrawiam.
Dlaczego potrzebny jest fragment:
tmp = tab[i];
tab[i] = tab[mini];
tab[mini] = tmp;
??
To jest zamiana wartości w komórkach. Robione jest to w trzech krokach aby przy zamianie liczb w komórkach tablicy zachować obie wartości.
Cześć, dlaczego po sortowaniu String[] imiona = {„Ola”, „Ala”, „ala”, „Ula”, „Aga”, „Iga”}; „ala” napisane małą literą, ląduje na końcu: Sortowanie: [Aga, Ala, Iga, Ola, Ula, ala], mimo że zaczyna się na literę a?
Ponieważ przy sortowaniu najpierw są duże litery, potem małe.
Można sprawdzić:
String[] imiona = {"a", "A", "b", "B", "C", "c"};
Pozdrawiam!
G.