Faragiri

همهٔ زبان‌ها

آموزش کالکشن‌های جاوا جاوا

سمت چپ انتساب همیشه اینترفیس است و سمت راست پیاده‌سازی. همان یک عادت، عوض کردن ظرف را بعدا به یک کلمه تبدیل می‌کند.

۴بخش
۳دقیقه خواندن
۴عمق

یک نگاشت درهم‌ساز

چه چیزی تضمین شده، چه چیزی نه، و چه چیزی به شما بسته است

هر بله پایین یک تضمین مستند است. سطر نامعلوم همان است که بیشتر باگ‌های این صفحه از آن می‌آیند.

  1. بلهخواندن با کلید در زمان ثابتبه‌طور میانگین، و نه در بدترین حالتبله
  2. بلهکلید تکراری روی قبلی می‌نویسدو مقدار قبلی را برمی‌گرداند، اگر بخواهیدشبله
  3. خیرمقدار تهی مجاز استدر این پیاده‌سازی بله، و در بعضی پیاده‌سازی‌های دیگر نهخیر
  4. خیرترتیب پیمایشنامشخص، و می‌تواند بین دو اجرا فرق کندخیر
  5. خیرامن بودن برای چند رشته اجرانیست، و شکستنش هیچ خطایی نمی‌دهدخیر
  6. بررسی نشدهکلید شما equals و hashCode درست داردهیچ چیزی بررسی‌اش نمی‌کند و فقط شما می‌دانیدبررسی نشده

حالت سوم دلیل وجود این فهرست است. سطر آخر تنها چیزی است که خود زبان درباره‌اش ساکت است، و شکستنش به‌شکل مقداری بیرون می‌آید که گذاشتید و پیدا نمی‌شود.

یک قاعده که بیشتر از هر چیز دیگری در این بخش به کار می‌آید: نوع متغیر را اینترفیس بگذارید و فقط سازنده را از پیاده‌سازی بگیرید.

  • List<Qet> qetat = new ArrayList<>();
  • Map<String, Integer> mojoodi = new HashMap<>();
  • Set<String> kodha = new HashSet<>();

حالا اگر بعدا لازم شد ترتیب درج حفظ شود یا دسترسی هم‌زمان امن باشد، فقط سمت راست عوض می‌شود و هیچ‌کدام از متدهایی که این متغیر را می‌گیرند دست نمی‌خورند.

یک نکته ساختاری که تازه‌کارها را غافلگیر می‌کند: Map کلید را به مقدار نگاشت می‌کند و اصلا زیرمجموعه Collection نیست. برای همین متدهایی که روی فهرست و مجموعه کار می‌کنند روی نگاشت کار نمی‌کنند، و برای پیمایشش باید یکی از سه نمای آن را بگیرید: کلیدها، مقدارها، یا مدخل‌ها.

چیزی که HashMap قول می‌دهد و چیزی که نمی‌دهد

قولش این است: افزودن و خواندن با کلید، به‌طور میانگین در زمان ثابت. چیزی که قول نمی‌دهد ترتیب است. ترتیب پیمایش نامشخص است و می‌تواند بین دو اجرا فرق کند، پس هر کدی که به ترتیب خروجی یک نگاشت درهم‌ساز تکیه کند دیر یا زود می‌شکند و آزمونی هم که امروز سبز است فردا قرمز می‌شود.

اگر ترتیب لازم دارید صریح بخواهیدش: LinkedHashMap ترتیب درج را نگه می‌دارد و TreeMap بر اساس کلید مرتب نگه می‌دارد. هر دو همان اینترفیس را پیاده می‌کنند، پس تغییر یک کلمه است.

کلید باید equals و hashCode درست داشته باشد

این تنها شرطی است که خود زبان بررسی نمی‌کند و شکستنش بی‌صدا است. اگر شی‌ای را کلید کنید که این دو را ندارد، دو شی با محتوای یکسان دو کلید جدا می‌شوند و مقداری که گذاشته‌اید دیگر پیدا نمی‌شود. رکورد این دو را خودش می‌سازد و دقیقا به همین دلیل کلید خوبی است.

خطای بدتر این است که شی‌ای را کلید کنید و بعد فیلدی از آن را عوض کنید: درهم‌ساز آن را در سطل قبلی گذاشته بود و حالا در سطل دیگری دنبالش می‌گردد. کلید باید تغییرناپذیر باشد، و همان قاعده‌ای است که در پایتون هم حاکم است: جست‌وجو با کلید، و عضویت در زمان ثابت.

جایی که کار گیر می‌کند، فهرست به‌جای نگاشت است

رایج‌ترین الگوی کند در کدی که به یک تیم می‌رسد این است: یک فهرست از اشیا و یک حلقه که هر بار داخلش دنبال یک کد می‌گردد. روی صد قلم هیچ نشانه‌ای نمی‌دهد و روی صد هزار قلم گزارش شبانه تمام نمی‌شود. تشخیصش ارزان است: هر حلقه‌ای که داخلش یک حلقه دیگر روی فهرست دوم است یا از contains روی فهرست استفاده می‌کند، همین است. تبدیلش هم یک سطر است، یعنی ساختن یک نگاشت از کد به شی پیش از حلقه. زبان شمردن این تفاوت در شمردن کار، وقتی ورودی بزرگ می‌شود است.

ابزار، و سالی که این حرف درست است

در سال ۲۰۲۶، برای فهرست‌های ثابتی که پس از ساخت عوض نمی‌شوند، متدهای کارخانه‌ای مثل List.of و Map.of در خود زبان‌اند و از Java 9 در دسترس؛ جای بسته‌بندی دستی با متدهای قدیمی را گرفته‌اند و چیزی که برمی‌گردانند واقعا تغییرناپذیر است، یعنی افزودن به آن استثنا می‌دهد. برای پردازش، جریان‌ها از Java 8 در دسترس‌اند و برای جمع کردن نتیجه در یک فهرست تغییرناپذیر، toList از Java 16 کوتاه‌ترین شکل است. همه اینها بخشی از کتابخانه استانداردند و وابستگی بیرونی ندارند.

پرسش بعدی این است که این ظرف‌ها وقتی چند رشته اجرا هم‌زمان به آن‌ها دست بزنند چه می‌شوند، که دو کار هم‌زمان، و باگ‌هایی که فقط از همین می‌آیند است. و انتخاب میان شکل‌های داده، مستقل از زبان، در انتخاب شکلی که مسئله را ساده می‌کند است.

کنار این

بالای این