1. Kolleksiyaların dəyişkənliyi problemi
Java-da kolleksiyalar anbardakı malları xatırladır: hər kəs gəlib nəsə əlavə edə, çıxara, dəyişə bilər. Bəzən bu rahatdır, amma böyük proqramlarda bu, başağrısına çevrilir. Təsəvvür edin ki, sinfinizdən mal siyahısını kənara verdiniz, kimsə isə götürüb yarısını sildi. Və — daha da “əyəncəli” — çox axınlı proqramda bir axın elementlər əlavə edir, digəri isə oxuyur: nəticə gözlənilməz ola bilər, səhvlər isə tutması çətin (məsələn, ConcurrentModificationException).
Budur, niyə kolleksiyaların dəyişkənliyi xətaların mənbəyidir:
import java.util.*;
public class Inventory {
private List<String> products = new ArrayList<>();
public Inventory() {
products.add("Çay");
products.add("Qəhvə");
}
public List<String> getProducts() {
// TEHLÜKƏLİ! Daxili siyahıya istinadı qaytarırıq
return products;
}
}
public class Main {
public static void main(String[] args) {
Inventory inv = new Inventory();
List<String> external = inv.getProducts();
external.remove("Çay"); // Hop! İndi inventarda çay yoxdur
System.out.println(inv.getProducts()); // [Qəhvə]
}
}
Finti gördünüzmü? Bir metod daxili kolleksiyanı qaytarır, digəri isə onu dəyişir. Beləcə, qorunmalı olan məlumatlar təsadüfən məhv edilə bilər.
2. Dəyişdirilməyən kolleksiyaların yaradılması: Collections.unmodifiable*
Belə anlaşılmazlıqlardan qaçmaq üçün Java effektiv mühafizə təklif edir: kolleksiyanı xüsusi örtüklərlə “dəyişdirilməyən” etmək olar — bunlar Collections sinfindədir:
- Collections.unmodifiableList(list)
- Collections.unmodifiableSet(set)
- Collections.unmodifiableMap(map)
Bu necə işləyir? Əvvəl adi kolleksiya yaradırsınız, sonra onu “dəyişdirilməyən” örtüklə bürüyürsünüz:
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> drinks = new ArrayList<>();
drinks.add("Çay");
drinks.add("Qəhvə");
List<String> immutableDrinks = Collections.unmodifiableList(drinks);
System.out.println(immutableDrinks); // [Çay, Qəhvə]
// Element əlavə etməyə cəhd edək
immutableDrinks.add("Kakao"); // Bam! UnsupportedOperationException
}
}
Belə kolleksiyanı dəyişdirmə cəhdi UnsupportedOperationException istisnası atılmasına gətirib çıxarır. Bu, sanki qutunun üstünə böyük bir "TOXUNMAYIN!" stikerini yapışdırmısınız — kim nəsə əlavə etməyə və ya silməyə çalışsa, “əllərinə (və ya çağırış yığınının üstündə)” şillə dəyəcək.
Nümunə: daxili vəziyyəti qoruyuruq
Gəlin əvvəlki nümunədəki Inventory sinfini düzəldək:
import java.util.*;
public class Inventory {
private List<String> products = new ArrayList<>();
public Inventory() {
products.add("Çay");
products.add("Qəhvə");
}
public List<String> getProducts() {
// İndi örtük qaytarırıq
return Collections.unmodifiableList(products);
}
}
İndi kimsə əldə etdiyi siyahını dəyişməyə çalışsa, istisna alacaq.
3. Dəyişdirilməyən kolleksiyaların davranışı: səthi qoruma
Vacib məqam: unmodifiableList və “qardaşları” yalnız ilkin kolleksiyanın üzərinə örtük qoyur. Onlar surət yaratmır — daxildəki (yəni “içində olan”) kolleksiyaya edilən istənilən dəyişikliklər örtükdə də görünəcək!
Nümayiş
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> drinks = new ArrayList<>();
drinks.add("Çay");
List<String> immutableDrinks = Collections.unmodifiableList(drinks);
drinks.add("Qəhvə"); // Mənbə kolleksiyanı dəyişirik
System.out.println(immutableDrinks); // [Çay, Qəhvə] — element göründü!
}
}
Nəticə: örtük yalnız özündən dəyişikliklərə qarşı qoruyur. Kimsə mənbə kolleksiyanın istinadını saxlayırsa, onu yenə də dəyişə bilər.
4. Dərin dəyişdirilməzlik: miflər və reallıq
unmodifiable* örtükləri kolleksiyanı yalnız xaricdən dəyişdirilməz edir. Amma kolleksiya dəyişdirilə bilən obyektlər saxlayırsa, həmin obyektləri dəyişmək olar!
Nümunə
import java.util.*;
class Product {
String name;
Product(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
public class Main {
public static void main(String[] args) {
List<Product> products = new ArrayList<>();
products.add(new Product("Çay"));
List<Product> immutableProducts = Collections.unmodifiableList(products);
// Kolleksiya daxilində obyektə dəyişiklik edirik
immutableProducts.get(0).name = "Qəhvə";
System.out.println(immutableProducts); // [Qəhvə]
}
}
Nəticə:
- Kolleksiya “dəyişdirilməzdir”, amma içindəki obyektlər — yox.
- Tam (dərin) dəyişdirilməzlik üçün dəyişdirilməyən obyektlərdən istifadə edin (məsələn, String, Integer, record-siniflər) və ya öz siniflərinizi dəyişdirilməyən edin.
5. Dəyişdirilməyən kolleksiyalardan nə vaxt istifadə etməli
Daxili vəziyyəti qorumaq üçün
Əgər kolleksiya saxlayan sinif yazırsınızsa və onu kənara verirsinizsə, kimsənin məlumatlarınızı təsadüfən (və ya qəsdən) dəyişə bilməməsi üçün həmişə örtük qaytarın:
public List<String> getProducts() {
return Collections.unmodifiableList(products);
}
Çox axınlı proqramlarda
Çox axınlı tətbiqlərdə dəyişən kolleksiyalar problemlərin mənbəyidir (race condition, ConcurrentModificationException və digər “həyat sevinc(ləri)”). Kolleksiya yaradıldıqdan sonra dəyişməməlidirsə — onu dəyişdirilməyən edin.
Laylar arasında məlumat ötürmək üçün
Kolleksiyanı proqramın bir layından digərinə ötürürsünüzsə (məsələn, DAO-dan servisa), təsadüfi dəyişikliklərdən qorunmaq üçün dəyişdirilməyən surəti və ya örtüyü ötürün.
6. Praktik nümunələr
Nümunə 1: tələbələr siyahısını qoruyuruq
import java.util.*;
public class Group {
private final List<String> students = new ArrayList<>();
public void addStudent(String name) {
students.add(name);
}
public List<String> getStudents() {
return Collections.unmodifiableList(students);
}
}
İndi heç kim getStudents() vasitəsilə birbaşa tələbə əlavə edə və ya silə bilməz.
Nümunə 2: dəyişdirilməyən xəritə (Map)
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<String, Integer> grades = new HashMap<>();
grades.put("Vasya", 5);
grades.put("Masha", 4);
Map<String, Integer> immutableGrades = Collections.unmodifiableMap(grades);
// immutableGrades.put("Petya", 3); // UnsupportedOperationException
}
}
7. Faydalı nüanslar
Müasir alternativlər: List.of, Set.of, Map.of
Java 9-da dəyişdirilməyən kolleksiyaları yaratmağın daha da rahat yolları meydana çıxdı:
List<String> drinks = List.of("Çay", "Qəhvə");
Set<String> fruits = Set.of("Alma", "Banan");
Map<String, Integer> ages = Map.of("Vasya", 20, "Masha", 21);
- Bu kolleksiyalar dəyişdirilməzdir (istənilən dəyişiklik cəhdi — istisna).
- Onların “mənbə” dəyişən kolleksiyası yoxdur ( Collections.unmodifiable* ilə fərqli olaraq).
- null dəyərinə icazə vermirlər.
Həmçinin Java 10-dan etibarən List.copyOf, Set.copyOf, Map.copyOf kimi surət metodları var — verilən kolleksiyanın dəyişdirilməyən surətini yaradır.
Dəyişdirilməyən kolleksiyaları yaratma üsullarının müqayisəsi
| Üsul | Dərin dəyişdirilməzlik | Mənbə kolleksiyanı dəyişmək mümkündürmü? | null-a icazə verirmi? | Java versiyası |
|---|---|---|---|---|
|
Yox | Bəli | Bəli | 1.2 |
|
Yox | Yox (mənbə kolleksiya yoxdur) | Yox | 9+ |
8. Dəyişdirilməyən kolleksiyalarla işləyərkən tipik səhvlər
Səhv №1: Örtük yaradıldıqdan sonra mənbə kolleksiyanı dəyişmək. Siz unmodifiableList yaratdınız, sonra isə kimsə mənbə siyahını dəyişir. Örtük buna qarşı qorumur — dəyişikliklər örtüyün istifadə olunduğu bütün yerlərdə görünəcək.
Səhv №2: Dərin dəyişdirilməzliyi gözləmək. Bir çoxları düşünür ki, əgər kolleksiya dəyişdirilməzdirsə, deməli onun daxilindəki obyektləri də dəyişmək olmaz. Əslində yalnız struktur (əlavəetmə/silmə/kolleksiya vasitəsilə dəyişiklik) qorunur, obyektlərin məzmunu isə yox.
Səhv №3: Müasir fabriklərdə null dəyəri ilə dəyişdirilməyən kolleksiyalardan istifadə. List.of, Set.of, Map.of vasitəsilə yaradılan kolleksiyalar null-a icazə vermir. null əlavə etməyə və ya əldə etməyə cəhd istisnaya gətirib çıxaracaq.
Səhv №4: Dəyişən kolleksiyalara istinadların kənara ötürülməsi. Əgər daxili kolleksiyaya (örtüksüz) kənara istinad qaytarırsınızsa, məlumatlar üzərində nəzarəti itirirsiniz — bu, xətalara və invariantların “sızmasına” birbaşa yoldur.
Səhv №5: Dəyişənlik gözləyən koddakı dəyişdirilməyən kolleksiyalardan istifadə. Əgər xarici kod kolleksiyanı dəyişməyə (məsələn, element əlavə etməyə) cəhd edərsə, UnsupportedOperationException alacaq. İstehlakçıların məlumatların dəyişdirilməz olduğunu bildiyinə əmin olun.
GO TO FULL VERSION