1. Giriş
Klassik kolleksiyalar: çeviklik və tələlər
new ArrayList<>() vasitəsilə kolleksiya yaratdıqda, siz istədiyiniz kimi dəyişdirə biləcəyiniz bir struktura sahib olursunuz: elementləri əlavə etmək, silmək, dəyişmək olar. Bu, məlumatları «yerindəcə» quranda rahatdır. Amma bəs bu kolleksiyanı dəyişdirilməməli olduğu başqa bir sinfə və ya metoda ötürsəniz nə olar? Ya da kolleksiyanı təsadüfən kənara ötürüb kimsə onu dəyişsə? Bax, problemlər də burada başlayır.
Klassik səhv nümunəsi
import java.util.*;
public class Example {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
// Kolleksiyanı "kənara" ötürürük
processNames(names);
// Siyahının dəyişmədiyini gözləyirik...
System.out.println(names);
}
public static void processNames(List<String> list) {
// Kimsə götürüb elementi sildi!
list.remove("Bob");
}
}
Çıxış:
[Alice, Charlie]
Kolleksiyanız dəyişdi, halbuki bunu ümumiyyətlə planlaşdırmırdınız. Böyük layihələrdə belə «sürprizlər» asanlıqla son dərəcə xoşagəlməz və çətin izlənən xətalara çevrilir.
Təhlükə ondadır ki, kolleksiya ilə işləyən kod gözlənilməz məlumat dəyişiklikləri ilə üzləşə bilər. Üstəlik, məlumat itkisi riski həmişə var — kimsə təsadüfən elementi silir və ya üzərindən yazır. Kolleksiyanı eyni vaxtda müxtəlif axınlar dəyişsə, təkcə ConcurrentModificationException deyil, daha hiyləgər bir problem — qeyri‑konsistent məlumatlarla da qarşılaşa bilərsiniz.
Kolleksiyaların qorunması: köhnə yanaşma
Java 9‑dan əvvəl Collections.unmodifiableList(...) kimi wrapper metodlardan istifadə etmək lazım gəlirdi; bu barədə əvvəlki səviyyədə danışmışdıq. Onlar «dondurulmuş» kolleksiya qaytarmağa kömək edir. Amma bu üsul həmişə rahat deyil və bütün problemləri həll etmir (bunun haqqında ətraflı — növbəti mühazirədə).
2. Müasir həll: fabrik metodları List.of, Set.of, Map.of
Java 9‑da kolleksiya interfeyslərində yeni statik metodlar peyda oldu: List.of, Set.of, Map.of. Onlar dəyişdirilə bilməyən kolleksiyanı tez və rahat yaratmağa imkan verir. Sanki kolleksiyanı hazırlayıb dərhal betonla tökmüsünüz — heç kim element əlavə edə, silə və ya dəyişə bilməz.
Dəyişməz kolleksiyaların yaradılması nümunəsi
import java.util.*;
public class ImmutableDemo {
public static void main(String[] args) {
List<String> names = List.of("Alice", "Bob", "Charlie");
Set<Integer> numbers = Set.of(1, 2, 3);
Map<String, Integer> ages = Map.of("Alice", 30, "Bob", 25, "Charlie", 28);
System.out.println(names);
System.out.println(numbers);
System.out.println(ages);
}
}
Çıxış:
[Alice, Bob, Charlie]
[1, 2, 3]
{Alice=30, Bob=25, Charlie=28}
Bu necə işləyir?
- List.of(...) — dəyişməz siyahı yaradır.
- Set.of(...) — dəyişməz kümə yaradır.
- Map.of(...) — dəyişməz xəritə yaradır (10 açar‑dəyər cütünə qədər; daha çoxu üçün Map.ofEntries(...) istifadə edin).
Diqqət! Bu metodlarla yaradılan kolleksiyalarda dəyişiklik etmək olmaz. Hər hansı elementi əlavə etmək, silmək və ya əvəzləmək cəhdi istisnaya səbəb olacaq.
3. İstifadə nümunələri və «gizli tələlər»
Nümunə: kolleksiyanı dəyişməyə cəhd
import java.util.*;
public class ImmutableFail {
public static void main(String[] args) {
List<String> names = List.of("Alice", "Bob");
// names.add("Charlie"); // İcra zamanı xəta!
try {
names.add("Charlie");
} catch (UnsupportedOperationException ex) {
System.out.println("Element əlavə etmək olmaz: " + ex.getClass().getSimpleName());
}
}
}
Çıxış:
Element əlavə etmək olmaz: UnsupportedOperationException
Nümunə: null əlavə etmək
import java.util.*;
public class NullFail {
public static void main(String[] args) {
try {
List<String> badList = List.of("Alice", null, "Bob");
} catch (NullPointerException ex) {
System.out.println("null qadağandır: " + ex.getClass().getSimpleName());
}
}
}
Çıxış:
null qadağandır: NullPointerException
Nümunə: Set.of daxilində dublikatlar
import java.util.*;
public class DuplicatesFail {
public static void main(String[] args) {
try {
Set<String> badSet = Set.of("one", "two", "one");
} catch (IllegalArgumentException ex) {
System.out.println("Dublikatlar qadağandır: " + ex.getClass().getSimpleName());
}
}
}
Çıxış:
Dublikatlar qadağandır: IllegalArgumentException
Nümunə: çox sayda cütlüklə Map.of
import java.util.*;
public class MapOfLarge {
public static void main(String[] args) {
// Map.of açar‑dəyərdən 10 cütə qədər dəstəkləyir
Map<String, Integer> map = Map.of(
"one", 1, "two", 2, "three", 3, "four", 4, "five", 5,
"six", 6, "seven", 7, "eight", 8, "nine", 9, "ten", 10
);
System.out.println(map);
// Daha çoxu üçün Map.ofEntries istifadə edin
Map<String, Integer> bigMap = Map.ofEntries(
Map.entry("eleven", 11),
Map.entry("twelve", 12),
Map.entry("thirteen", 13)
// ... və s.
);
System.out.println(bigMap);
}
}
4. Dəyişməz kolleksiyaların xüsusiyyətləri və məhdudiyyətləri
Dəyişmək olmaz.
Hər hansı elementi əlavə etmək, silmək və ya dəyişmək cəhdi UnsupportedOperationException atacaq. Adətən icazəli olan metodlar da (add, remove, set) işləmir.
null istifadə etmək olmaz.
Əgər null-u siyahının və ya kümənin elementi, yaxud xəritənin açarı/dəyəri kimi əlavə etməyə çalışsanız, NullPointerException alacaqsınız. Bu, təhlükəsizlik üçündür: null elementləri kolleksiyalarda tez‑tez xətalara səbəb olur.
Konkret reallaşdırma zəmanətli deyil.
List.of və s. ilə yaradılan kolleksiyanın «qapağının altında» hansı sinfin dayandığını bilməyəcəksiniz. ArrayList üçün instanceof etmək və ya kolleksiyanı konkret tipə çevirməyə çalışmaq lazım deyil.
Elementlərin ardıcıllığı.
— List.of üçün elementlərin ardıcıllığı saxlanılır (adi siyahıda olduğu kimi).
— Set.of üçün ardıcıllıq zəmanətli deyil (praktikada arqumentlərin verilmə sırası ilə üst‑üstə düşə bilər, amma buna arxayın olmayın).
— Map.of üçün cütlərin ardıcıllığı zəmanətli deyil.
Performans.
Fabrik metodlarla yaradılan kolleksiyalar adətən dəyişən kolleksiyalar üzərindəki wrapper‑lardan daha sürətli olur, çünki artıq imkanlar üçün yaddaş xərcləmirlər.
5. Dəyişməz kolleksiyalardan nə vaxt və niyə istifadə etməli
Sabit məlumat dəstləri
Proqram işlədiyi müddətdə dəyişməməli olan siyahı, kümə və ya xəritəniz varsa, List.of, Set.of, Map.of istifadə edin. Məsələn:
private static final List<String> ROLES = List.of("USER", "ADMIN", "MODERATOR");
Artıq heç kim bu siyahıya əlavə rol daxil edə bilməyəcək.
Metodlardan kolleksiyaların qaytarılması
Metoddan kolleksiya qaytarırsınızsa və onun kənarda dəyişdirilməsini istəmirsinizsə:
public List<String> getDefaultNames() {
return List.of("Alice", "Bob", "Charlie");
}
Qəbul edən tərəf məlumatlarınızı korlaya bilməyəcək.
Tətbiq qatları arasında ötürmə
Proqramın müxtəlif hissələri (məsələn, veb‑tətbiqdə Controller və Service qatları) arasında kolleksiyaları ötürərkən, kimsənin onları «səssizcə» dəyişdirə bilməməsi üçün dəyişməz kolleksiyalardan istifadə etmək daha yaxşıdır.
Təhlükəsizlik və axın təhlükəsizliyi
Dəyişməz kolleksiyalar tərifcə oxu baxımından axınlar üçün təhlükəsizdir: əgər onları heç kim dəyişə bilmirsə, müxtəlif axınlardan sinxronizasiya olmadan rahatlıqla istifadə etmək mümkündür.
6. Ümumi tətbiq üçün praktiki nümunələr
Tutaq ki, tədris tətbiqimizdə dəstəklənən əmrlərin siyahısı var:
public class Commands {
public static final List<String> SUPPORTED_COMMANDS = List.of(
"help", "exit", "list", "add", "remove"
);
}
Belə etsəniz:
Commands.SUPPORTED_COMMANDS.add("hack_the_system");
İstisna alacaqsınız və tətbiqə zərər verə bilməyəcəksiniz.
Məsələn, səhv kodları olan bir xəritəniz varsa:
public class ErrorCodes {
public static final Map<Integer, String> CODES = Map.of(
404, "Not Found",
500, "Internal Server Error",
403, "Forbidden"
);
}
Yeni kod əlavə etmək cəhdi — istisna atacaq.
7. Kolleksiya yaratma yanaşmalarının müqayisəsi
| Yaradılma üsulu | Dəyişmək olar? | null olar? | Dublikatlar? | Axın təhlükəsizliyi | Nümunə |
|---|---|---|---|---|---|
|
Bəli | Bəli | Bəli | Xeyr | |
|
Xeyr | Xeyr | Bəli | Bəli* | |
|
Xeyr | Xeyr | Xeyr | Bəli* | |
|
Xeyr | Xeyr | Xeyr | Bəli* | |
|
Xeyr | İlkin kolleksiyadan asılıdır | Bəli | Xeyr | |
* — axın təhlükəsizliyi yalnız dəyişməzlik baxımındandır: kolleksiya dəyişdirilmirsə, onu müxtəlif axınlardan təhlükəsiz oxumaq olar.
8. List.of, Set.of, Map.of ilə işləyərkən tipik səhvlər
Səhv №1: kolleksiyanı dəyişməyə cəhd.
Çox yayılmış bir səhv — List.of, Set.of və ya Map.of ilə yaradılmış kolleksiyadan element əlavə etmək və ya silmək cəhdi. Məsələn, names.add("Dmitry") və ya ages.remove("Bob"). Bu, həmişə icra zamanı UnsupportedOperationException ilə nəticələnir.
Səhv №2: null əlavə etməyə cəhd.
Təsadüfən null-u hər hansı metoda versəniz (məsələn, List.of("Alice", null)), NullPointerException alacaqsınız. Java 9+ dəyişməz kolleksiyaları null-u sevmir — və bu, əslində, yaxşıdır.
Səhv №3: Set.of və ya Map.of‑da dublikat elementlər.
Set.of("a", "b", "a") və ya Map.of("x", 1, "x", 2) IllegalArgumentException atacaq. Kümə və xəritə tərifcə dublikat saxlamır.
Səhv №4: konkret reallaşdırmanı gözləmək.
Belə etmək lazım deyil:
List<String> list = List.of("a", "b");
if (list instanceof ArrayList) {
// ...
} // Bu həmişə false olacaq!
Daxili reallaşdırma gizlidir — reallaşdırma detallarına etibar etməyin.
Səhv №5: kolleksiyanı dəyişən metodlardan istifadə etməyə cəhd.
Hətta clear(), siyahıda set(index, value) kimi metodlar da istisna atacaq. Yadda saxlayın: fabrik metodları ilə yaradılan kolleksiyalar dəyişməzdir.
GO TO FULL VERSION