تسلط بر Java Collections
بسیاری از توسعهدهندگان به طور پیشفرض از ArrayList یا HashSet استفاده میکنند. این کار برای وظایف ساده جواب میدهد، اما زمانی که به سرعت یا مقیاسپذیری نیاز دارید، شکست میخورد.
من زمانی یک جدول امتیازات بازی را با استفاده از یک ArrayList ساده ساختم. هر بار که امتیازی تغییر میکرد، آن را مرتب میکردم. رابط کاربری (UI) مدام فریز میشد. من به جای استفاده از زبان، در حال جنگیدن با آن بودم.
استفاده از ابزارهای اشتباه را متوقف کنید. از این سه مجموعه (collection) تخصصی برای نوشتن کدی سریعتر و تمیزتر استفاده کنید.
۱. EnumSet برای ثابتهای Enum
اگر از HashSet برای enumها استفاده کنید، هزینهی عملکردی (performance tax) پرداخت میکنید. هر درج (insertion)، enum را در یک شیء بستهبندی (box) میکند. این کار سربار غیرضروری ایجاد میکند.
EnumSet از یک بردار بیت (bit vector) استفاده میکند. این مجموعه بررسیها را با استفاده از یک دستور واحد CPU انجام میدهد.
- زمانی که مجموعهای ثابت از مقادیر enum دارید، از آن استفاده کنید.
- باعث کاهش Garbage Collection میشود.
- در حلقههای فشرده، سرعت را تا ۱۰ برابر افزایش میدهد.
Before:
Set<Ability> abilities = new HashSet<>();
abilities.add(Ability.FIRE);
After:
EnumSet<Ability> abilities = EnumSet.of(Ability.FIRE);
۲. NavigableSet برای پرسوجوهای بازهای (Range Queries)
پیمایش دستی در یک لیست مرتبشده برای یافتن یک بازه، کند و مستعد خطا است. اغلب با باگهای off-by-one مواجه میشوید.
NavigableSet دادههای شما را به طور خودکار مرتب نگه میدارد. این مجموعه جستجوهای زیرمجموعه (subsets) را با پیچیدگی O(log n) انجام میدهد.
- برای جداول امتیازات بالا یا بازههای قیمتی از آن استفاده کنید.
- از
headSet()یاsubSet()برای گرفتن بازههای خاص استفاده کنید. - نیاز به کد مرتبسازی دستی را از بین میبرد.
Before:
Collections.sort(scores);
List<Integer> topTen = scores.subList(size - 10, size);
After:
NavigableSet<Integer> scores = new TreeSet<>(Comparator.reverseOrder());
scores.add(1542);
NavigableSet<Integer> topTen = scores.headSet(scores.first(), true).stream().limit(10).collect(Collectors.toCollection(TreeSet::new));
۳. CopyOnWriteArrayList برای لیستهای با خواندن بالا (Read-Heavy)
استفاده از بلوکهای synchronized روی یک ArrayList، سرعت هر عملیات خواندن را کاهش میدهد. همچنین اگر یک رشته (thread) در حال نوشتن باشد و رشته دیگری در حال خواندن، باعث بروز ConcurrentModificationException میشود.
CopyOnWriteArrayList با هر بار نوشتن، یک کپی جدید از آرایه ایجاد میکند. خوانندهها به یک تصویر لحظهای (snapshot) از آرایه نگاه میکنند.
- برای شنوندههای رویداد (event listeners) یا تنظیمات پیکربندی از آن استفاده کنید.
- زمانی که عملیات خواندن بسیار بیشتر از نوشتن است، از آن استفاده کنید.
- امکان خواندن بدون قفل (lock-free) را فراهم میکند.
Before:
List<String> log = Collections.synchronizedList(new ArrayList<>());
// Iterating here can crash if a writer joins in.
After:
CopyOnWriteArrayList<String> log = new CopyOnWriteArrayList<>();
// Iteration is safe and never crashes.
دیگر به طور پیشفرض از همان دو کالکشن استفاده نکنید. ابزاری را انتخاب کنید که با الگوی دادههای شما همخوانی داشته باشد.
منبع: https://dev.to/timevolt/the-java-collections-force-mastering-the-hidden-gems-like-a-jedi-4438