1. Giriş
Sadə dillə desək, mutable kolleksiya — yaradıldıqdan sonra onu dəyişmək mümkündür: elementləri əlavə etmək, silmək və dəyişmək olar. Immutable (dəyişməz) kolleksiya — yaradıldıqdan sonra dəyişdirilə bilməyən kolleksiyadır. Elə bərkimiş betona bənzəyir: baxmaq, toxunmaq olar, amma yeni fiqurlar “yoğurmaq” artıq mümkün deyil.
Mutable-kolleksiya — qələmlə dəftər kimidir: yazırsan, pozursan, yeni qeydlər əlavə edirsən. Immutable-kolleksiya — laminasiya etdiyiniz bir səhifə kimidir: artıq heç kim ora nə isə əlavə edə və ya silə bilməz.
Dəyişən (mutable) kolleksiyalara nümunələr
Java-da demək olar ki, bütün standart kolleksiyalar susmaya görə dəyişəndir. Məsələn, bu kimi siniflər:
- ArrayList
- LinkedList
- HashSet
- TreeSet
- HashMap
- LinkedHashMap
- və daha bir çoxu
Nümunə: ArrayList
import java.util.*;
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.set(1, "Charlie"); // Bob-u Charlie ilə əvəz etdik
names.remove("Alice"); // Alice-i sildik
System.out.println(names); // [Charlie]
Burada kolleksiya ilə istədiyimizi edə bilərik: əlavə etmək, silmək, elementlərin yerini dəyişmək. Bu, kolleksiya dinamik şəkildə qurulduqda, məsələn, fayldan oxuma və ya istifadəçi daxil etməsi zamanı əlverişlidir.
2. Dəyişməz (immutable) kolleksiyalara nümunələr
Java 9-da yaranan bu alətlərlə artıq tanışsınız, amma onlara hələ də öyrəşmək lazımdır:
- List.of(...)
- Set.of(...)
- Map.of(...)
- List.copyOf(collection)
- Set.copyOf(collection)
- Map.copyOf(map)
Nümunə: List.of
List<String> planets = List.of("Mercury", "Venus", "Earth", "Mars");
System.out.println(planets); // [Mercury, Venus, Earth, Mars]
planets.add("Jupiter"); // UnsupportedOperationException atacaq!
Kolleksiyanı dəyişdirməyə cəhd icra zamanı istisnaya gətirib çıxarır.
Nümunə: Collections.unmodifiableList
List<String> modifiable = new ArrayList<>(List.of("a", "b"));
List<String> unmodifiable = Collections.unmodifiableList(modifiable);
unmodifiable.add("c"); // UnsupportedOperationException!
Amma burada incə bir məqam var: əgər ilkin kolleksiyanı dəyişsəniz, örtük də dəyişəcək!
modifiable.add("c");
System.out.println(unmodifiable); // [a, b, c] — element əlavə olundu!
3. Mutable və immutable kolleksiyalar arasındakı əsas fərqlər
| Xüsusiyyət | Mutable (dəyişən) | Immutable (dəyişməz) |
|---|---|---|
| Element əlavə etmək mümkündürmü? | Bəli | Xeyr |
| Elementi silmək mümkündürmü? | Bəli | Xeyr |
| Elementi dəyişmək mümkündürmü? | Bəli (məsələn, set) | Xeyr |
| Çoxaxınlı təhlükəsizlik | Xeyr (susmaya görə) | Bəli (daxili vəziyyət yoxdur — dəyişdiriləsi heç nə yoxdur) |
| null əlavə etmək olar? | Bəli (adətən) | Xeyr (Java 9+ fabrik metodlarında) |
| Reallaşdırma | ArrayList, HashSet və s. | List.of, Set.of, Map.of, copyOf |
4. Dəyişməz kolleksiyalar ümumiyyətlə nə üçün lazımdır?
Dərhal sual yaranır: əgər dəyişən kolleksiyalar bu qədər çevikdirsə, niyə dəyişməzlərə ehtiyac var? Səbəblər çoxdur və hamısı təhlükəsizlik, oxunaqlıq və kodun proqnozlaşdırıla bilməsi ilə bağlıdır.
Təhlükəsizlik və səhvlərdən qorunma
Kolleksiyanı xaricə (məsələn, metoddən və ya sinifdən) verdikdə, heç kimin onun məzmununu təsadüfən dəyişməyəcəyinə əmin olmaq istəyirsiniz. Bu xüsusilə vacibdir, əgər kolleksiya ilkinləşdirmədən sonra dəyişməməli olan “vacib” məlumatları ehtiva edirsə.
Nümunə:
public class Team {
private final List<String> players;
public Team(List<String> players) {
// Heç kim heyəti dəyişə bilməsin deyə dəyişməz nüsxə yaradırıq
this.players = List.copyOf(players);
}
public List<String> getPlayers() {
return players;
}
}
Artıq oyunçular siyahısını alan istənilən kod ora “dostunu” əlavə edə bilməyəcək.
Çoxaxınlı təhlükəsizlik
Dəyişən kolleksiyalar eyni vaxtda bir neçə axından giriş üçün təhlükəsiz deyil. Dəyişməz kolleksiyalar isə əksinə, axınlar arasında sərbəst ötürülə bilər — heç kim onları korlaya bilməz.
Sazlamanı asanlaşdırmaq
Kolleksiya dəyişmirsə, həmişə orada nə olduğunu bilirsiniz. Kimsə onu kodun başqa yerində “səssizcə” dəyişib-deyişməməsindən narahat olmağa ehtiyac yoxdur.
Başqa kolleksiyalarda açar və ya dəyər kimi istifadə
Dəyişməz obyektlər Map-də açar, Set-də element kimi istifadə üçün ideal namizəddir. Obyekt əlavə edildikdən sonra dəyişə bilirsə, ona çıxışı itirmək riskiniz var (bax: hashCode və equals).
5. Dəyişən kolleksiyaları nə vaxt istifadə etmək daha yaxşıdır?
Dəyişən kolleksiyalar aşağıdakı hallarda əlverişlidir:
- Kolleksiya mərhələli şəkildə, dövrdə və ya müxtəlif mənbələrdən qurulursa.
- Tez-tez dəyişikliklər lazımdırsa: əlavə etmə, silmə, çeşidləmə.
- Kolleksiya daxili istifadə üçündür və “çöldən” heç kim onu poza bilməyəcək.
Nümunə: Siyahının qurulması
List<String> shoppingList = new ArrayList<>();
shoppingList.add("Süd");
shoppingList.add("Çörək");
shoppingList.add("Almalar");
// Qurulduqdan sonra — dəyişməz versiyasını yaratmaq olar
List<String> finalList = List.copyOf(shoppingList);
6. Dəyişməz kolleksiyaları nə vaxt istifadə etmək daha yaxşıdır?
- Sabit məlumatları saxlamaq üçün (məsələn, həftənin günləri siyahısı).
- Tətbiqin qatları arasında kolleksiyaları ötürmək üçün (məsələn, DAO-dan servisə).
- Kolleksiyaları metodlardan qaytararkən onları dəyişikliklərdən qorumaq üçün.
- Çoxaxınlı ssenarilərdə təhlükəsizlik vacib olduqda.
Nümunə: Sabit məlumatlar
public static final List<String> WEEKDAYS = List.of(
"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"
);
Nümunə: Çölə ötürmə
public List<String> getReadOnlyNames() {
return List.copyOf(names); // heç kim siyahını dəyişə bilməyəcək
}
7. Xüsusiyyətlər və gizli tələlər
Dəyişməzlik ≠ Çoxaxınlı təhlükəsizlik
Dəyişməz kolleksiya dəyişikliklərdən qorunur, amma bu, çoxaxınlı mühitdə digər problemlərdən qorunduğu anlamına gəlmir (məsələn, kolleksiyanın elementləri dəyişən obyektlərdirsə).
List<List<String>> listOfLists = List.of(new ArrayList<>());
listOfLists.get(0).add("Oops!"); // Daxili siyahını dəyişmək olar!
Örtüklər vs nüsxələr
Daha əvvəl müzakirə etdiyimiz kimi, Collections.unmodifiableList — sadəcə örtükdür və əgər ilkin kolleksiya dəyişdirilsə, örtük də dəyişəcək. List.copyOf isə həqiqi, müstəqil nüsxə yaradır.
List<String> base = new ArrayList<>(List.of("a", "b"));
List<String> wrap = Collections.unmodifiableList(base);
List<String> copy = List.copyOf(base);
base.add("c");
System.out.println(wrap); // [a, b, c] — dəyişdi!
System.out.println(copy); // [a, b] — əvvəlki kimi qaldı!
NullPointerException
Fabrik metodları (List.of, Set.of, Map.of) null əlavə etməyə imkan vermir:
List<String> bad = List.of("a", null); // Artıq yaradılma zamanı NullPointerException atacaq!
8. Yanaşmaların müqayisəsi: üstünlüklər və çatışmazlıqlar
Dəyişən kolleksiyalar
Dəyişən kolleksiyaların əsas üstünlüyü — çevikliyidir. Elementləri “yolda” əlavə edib silə, proqram məntiqi inkişaf etdikcə kolleksiyanı yenidən qura bilərsiniz. Bu yanaşma xüsusilə müvəqqəti strukturu tez toplamaq və ya nəyisə dinamik dəyişmək lazım olanda rahatdır.
Amma rahatlığın da qiyməti var. Dəyişən kolleksiya həmişə təsadüfi müdaxilə riski daşıyır: koddakı kimsə məlumatları bilmədən dəyişə bilər və bu, izlənməsi çətin xətalara gətirib çıxarar. Çoxaxınlı proqramlarda vəziyyət daha da pisdir — paralel dəyişikliklər asanlıqla yarış şəraitinə (race conditions) və gözlənilməz nasazlıqlara səbəb olur. Belə məlumatların həyat dövrünə nəzarət də çətindir: kim və nə zaman kolleksiyanı dəyişə bilər — bunu daim yadda saxlamaq lazımdır.
Dəyişməz kolleksiyalar
Dəyişməz kolleksiyalarla işləmək daha rahatdır. Dəqiq bilirsiniz ki, onları heç kim “arxadan” dəyişməyəcək. Bu, kodu daha təhlükəsiz edir, onu sazlamaq və test etmək asanlaşır, həmçinin bu kolleksiyaları əlavə bloklamalara ehtiyac olmadan axınlar arasında ötürmək rahatdır.
Eks tərəfi odur ki, bəzən məhsuldarlıqdan güzəştə getmək lazım gəlir. Yeni element əlavə etmək üçün çox vaxt kolleksiyanın yeni nüsxəsini yaratmaq lazım olur ki, bu da əlavə yaddaş və vaxt xərcləri deməkdir. Bundan başqa, böyük və mürəkkəb strukturları dərhal dəyişməz formada yığmaq bəzən əlverişli olmur. Adətən onlar müvəqqəti dəyişən kolleksiyada qurulur, hər şey hazır olanda isə “dondurulur”.
9. Tipik səhvlər
Səhv № 1: Dəyişən kolleksiyanın çölə qaytarılması. Metoddan adi ArrayList qaytarırsınızsa, istənilən xarici kod elementləri əlavə edib silə bilər. Bu isə izləması çox çətin olan xətalara səbəb ola bilər.
Səhv № 2: Nüsxə əvəzinə örtükdən istifadə. Əgər Collections.unmodifiableList istifadə edirsinizsə, amma ilkin kolleksiya haradasa dəyişdirilirsə, “dəyişməzlik” — illüziyadır.
Səhv № 3: Immutable kolleksiyalar daxilində dəyişən obyektlər. Kolleksiyanın özü dəyişməz olsa belə, onun elementləri dəyişən ola bilər. Bu, vəziyyətin gözlənilməz dəyişməsinə səbəb ola bilər.
Səhv № 4: Fabrik metodları ilə yaradılmış kolleksiyaya null əlavə etməyə cəhd. Köhnə kolleksiyalardan fərqli olaraq, yeni fabrik metodları null əlavə etməyə imkan vermir — NullPointerException alacaqsınız.
Səhv № 5: Konkret implementasiyanı gözləmək. List.of və ya Set.of vasitəsilə yaradılan kolleksiyalar implementasiya tipini zəmanət vermir (bu, mütləq ArrayList və ya HashSet deyil). Buna arxayın olmayın.
GO TO FULL VERSION