6. درباره لیست به ما بگویید
لیست رابطی است که ساختار مرتبی از اشیاء را نشان می دهد که به آن لیست می گویند. "ترفند" این ساختار این است که عناصر موجود در لیست را می توان با فهرست، یعنی شناسه داخلی لیست ، درج، اصلاح یا حذف کرد . به عبارت دیگر، ایندکس به این معنی است: "چند عنصر از ابتدای لیست وجود دارد." عنصر لیست اول دارای اندیس 0، دومی دارای شاخص 1 و غیره است. بنابراین عنصر پنجم چهار عنصر با ابتدای لیست فاصله دارد. همانطور که در بالا ذکر شد، ترتیب اضافه شدن موارد به یک لیست مهم است. به همین دلیل است که ساختار داده را لیست می نامند . ما روش های منحصر به فرد این ساختار را فهرست می کنیم که هدف آنها کار با عناصر بر اساس شاخص است:- get - عنصر را در موقعیت مشخص شده برمی گرداند (بر اساس مقدار شاخص)،
- حذف - عنصر را در موقعیت مشخص شده حذف می کند،
- set - عنصر را در موقعیت مشخص شده با عنصر مشخص شده در روش جایگزین می کند.
- فشار - عنصر ارسال شده را به بالای پشته اضافه می کند.
- peek - عنصری را که در بالای پشته قرار دارد برمی گرداند.
- pop - همچنین عنصری را که در بالای پشته است برمی گرداند، اما آن را حذف می کند.
- vala - خالی بودن پشته را بررسی می کند - درست است یا نه - نادرست است .
- جستجو - پشته را برای یک عنصر مشخص جستجو می کند. اگر عنصری پیدا شود، شماره دنباله آن نسبت به بالای پشته برگردانده می شود. اگر عنصر پیدا نشد، مقدار -1 برگردانده می شود.
7. درباره نقشه به ما بگویید
همانطور که در بالا گفته شد، نقشه مجموعه ای است که دارای ساختار مجزایی از رابط ها و پیاده سازی آنها است. جدا است زیرا در اینجا مقادیر یک به یک ذخیره نمی شوند، بلکه در یک جفت "کلید-مقدار" ذخیره می شوند. روش های اصلی نقشه :- put (کلید K، مقدار V) - اضافه کردن یک عنصر به نقشه.
- get (کلید شی) - یک مقدار را با کلید جستجو کنید.
- containKey (کلید Object) - نقشه را برای وجود این کلید بررسی می کند.
- containValue(ارزش شی) - نقشه را برای وجود این مقدار بررسی می کند.
- حذف (کلید شی) - حذف یک مقدار توسط کلید آن.
- HashMap - برای ذخیره مقادیر به ترتیب تصادفی طراحی شده است، اما به شما امکان می دهد عناصر نقشه را به سرعت جستجو کنید. به شما امکان می دهد با استفاده از کلمه کلیدی null یک کلید را مشخص کنید ، اما نه بیش از یک بار، زیرا جفت هایی با کلیدهای یکسان روی هم نوشته می شوند. شرط اصلی منحصر به فرد بودن کلیدها است: مقادیر را می توان تکرار کرد (ممکن است چندین مقدار تهی وجود داشته باشد).
- LinkedHashMap یک آنالوگ از HashMap است که مقادیر را به ترتیبی که اضافه شده اند ذخیره می کند. بر این اساس، مانند LinkedList ، دارای یک سرصفحه است - سر یک لیست با پیوند دوگانه. وقتی مقداردهی اولیه می شود، به خودش اشاره می کند.
LinkedHashMap همچنین دارای یک accessOrder است که نحوه دسترسی به عناصر را هنگام استفاده از تکرارکننده مشخص می کند. اگر accessOrder نادرست باشد، دسترسی به ترتیبی که عناصر درج شده اند انجام می شود. اگر درست باشد، عناصر به ترتیب آخرین دسترسی خواهند بود (آخرین عنصر دسترسی در انتها قرار می گیرد).
- TreeMap نقشه ای است که عناصر را بر اساس مقادیر کلیدی مرتب می کند. مشابه TreeSet ، اما برای جفت ها بر اساس مقادیر کلیدی. برای تنظیم قوانین مرتبسازی TreeMap ، کلیدها باید رابط Comparable را پیادهسازی کنند . در غیر این صورت، باید یک مقایسه کننده Key-oriented (که در سازنده TreeMap مشخص شده است) وجود داشته باشد ، یک TreeSet - که با یک شی TreeMap در داخل آن پیاده سازی شده است، که در واقع، همه جادوها در آن اتفاق می افتد.
میتوانید در مقاله ویژگیهای TreeMap درباره مرتبسازی در TreeMap با استفاده از درختهای قرمز-مشکی بیشتر بخوانید .
- Hashtable شبیه HashMap است ، اما اجازه نمی دهد null ها را به عنوان کلید یا مقادیر ذخیره کنید. از نقطه نظر چند رشته ای با دقت همگام می شود، که به نوبه خود به این معنی است که از نظر چند رشته ای ایمن است. اما این پیاده سازی قدیمی و کند است، بنابراین اکنون Hashtable را در پروژه های کم و بیش جدید نخواهید دید .
8. ArrayList در مقابل LinkedList. استفاده از کدام یک ارجح است؟
این سوال شاید محبوب ترین سوال در ساختار داده باشد و مشکلاتی را به همراه دارد. قبل از پاسخ دادن به آن، بیایید در مورد این ساختارهای داده بیشتر بیاموزیم. ArrayList رابط List را پیاده سازی می کند و بر روی یک آرایه داخلی که در صورت نیاز گسترش می یابد عمل می کند. هنگامی که آرایه داخلی به طور کامل پر شد، و یک عنصر جدید باید درج شود، یک آرایه جدید با اندازه (oldSize * 1.5) +1 ایجاد می شود. پس از این، تمام دادههای آرایه قدیمی در عنصر جدید + جدید کپی میشود و قدیمی توسط جمعآورنده زباله حذف میشود . متد add یک عنصر را به آخرین سلول خالی آرایه اضافه می کند. یعنی اگر از قبل 3 عنصر در آنجا داشته باشیم، عنصر بعدی را به سلول 4 اضافه می کند. بیایید عملکرد روش های اصلی را مرور کنیم:- get(int index) - گرفتن یک عنصر در یک آرایه توسط شاخص سریعترین در O(1) است .
- add(Object obj) - اگر فضای کافی در آرایه داخلی برای یک عنصر جدید وجود داشته باشد، با درج معمولی O(1) زمان صرف می شود ، زیرا جمع به آخرین سلول هدف قرار می گیرد.
اگر نیاز به ایجاد یک آرایه جدید و کپی کردن مطالب در آن داشته باشیم، زمان ما مستقیماً با تعداد عناصر آرایه O(n) متناسب خواهد بود .
- remove(int index) - هنگام حذف یک عنصر، به عنوان مثال، از وسط، زمان O(n/2) را دریافت می کنیم، زیرا باید عناصر را به سمت راست آن یک سلول به عقب ببریم. بر این اساس، اگر از ابتدای لیست حذف شود، O(n)، از انتها - O(1)؛
- add(int index, Object obj) - وضعیتی شبیه به حذف: هنگام اضافه کردن به وسط، باید عناصر را در یک سلول سمت راست به جلو حرکت دهیم، بنابراین زمان O(n/2) است. البته از ابتدا - O(n)، از پایان - O(1);
- set(int index, Object obj) - در اینجا وضعیت متفاوت است، زیرا شما فقط باید عنصر مورد نظر را پیدا کنید و بدون جابجایی بقیه روی آن بنویسید، بنابراین O(1).
- get(int index) - هنگام جستجوی عنصری که در وسط لیست قرار دارد، شروع به جستجو در تمام عناصر به ترتیب می کند تا زمانی که عنصر مورد نظر پیدا شود. به طور منطقی، جستجو باید O(n/2) باشد ، اما LinkedList نیز دارای یک دنباله است، بنابراین جستجو به طور همزمان از هر دو طرف انجام می شود. بر این اساس، زمان به O(n/4) کاهش می یابد .
اگر عنصر به ابتدای لیست یا انتهای آن نزدیک باشد، زمان O(1) خواهد بود .
- add(Object Obj) - هنگام اضافه کردن یک عنصر جدید، عنصر "دم" پیوندی به عنصر بعدی اضافه می کند و عنصر جدید پیوندی به این عنصر قبلی دریافت می کند و به "دم" جدید تبدیل می شود. بر این اساس، زمان O(1) خواهد بود .
- remove(int index) - منطقی شبیه به روش get(int index) . برای حذف یک عنصر از وسط لیست، ابتدا باید آن را پیدا کنید. این دوباره O(n/4) است ، در حالی که خود حذف در واقع چیزی نمی گیرد، زیرا فقط نشانگر اشیاء همسایه را تغییر می دهد (آنها شروع به ارجاع به یکدیگر می کنند). اگر عنصر در ابتدا یا در پایان باشد، دوباره - O(1) ;
- add(int index, Object obj) و set(int index, Object obj) - این روش ها پیچیدگی زمانی مشابه با get(int index) خواهند داشت ، زیرا بیشتر زمان صرف جستجوی عنصر می شود. بنابراین، برای وسط لیست - O(n/4) ، برای شروع - O(1).
عمل | ArrayList | LinkedList |
---|---|---|
دریافت بر اساس فهرست دریافت (شاخص) | O (1) | در وسط O(n/4) |
افزودن یک عنصر جدید add(obj) |
O (1) اگر نیاز به کپی کردن یک آرایه دارید، - O(n) |
O (1) |
حذف عنصر حذف (int index) |
از ابتدا - O(n) از وسط - O(n/2) از پایان - O (1) |
در وسط - O(n/4) در پایان یا در آغاز - O(n) |
افزودن عنصر add (int index, Object obj) |
بازگشت به بالا - O(n) تا وسط - O(n/2) تا پایان - O (1) |
در وسط - O(n/4) در پایان یا در آغاز - O(n) |
جایگزینی مجموعه عناصر (شاخص، obj) | O (1) |
در وسط - O(n/4) در پایان یا در آغاز - O(n) |
- بهترین انتخاب اگر متداول ترین عملیات جستجوی یک عنصر، بازنویسی یک عنصر باشد.
- بدترین انتخاب اگر عملیات درج و حذف در ابتدا و وسط باشد، زیرا عملیات جابجایی عناصر در سمت راست انجام خواهد شد.
- بهترین انتخاب اگر عملیات مکرر ما درج و حذف در ابتدا و میانه باشد.
- بدترین انتخاب اگر رایج ترین عملیات جستجو باشد.
9. عناصر در HashMap چگونه ذخیره می شوند؟
مجموعه HashMap شامل یک جدول آرایه داخلی Node[] است که سلول های آن را سطل نیز می نامند . گره حاوی:- کلید - پیوند به کلید،
- ارزش - ارجاع به مقدار،
- هش - ارزش هش،
- بعدی - پیوند به گره بعدی .
- کلید برای null بررسی می شود . اگر تهی باشد ، کلید در سلول جدول[0] ذخیره می شود زیرا کد هش برای null همیشه 0 است.
- اگر کلید تهی نباشد، متد ()hashcode شی کلید فراخوانی می شود که کد هش آن را برمی گرداند. این کد هش برای تعیین سلول آرایه ای که شی Node در آن ذخیره می شود استفاده می شود.
- در مرحله بعد، این هش کد در متد hash() داخلی قرار می گیرد که هش کد را محاسبه می کند، اما در اندازه آرایه جدول[] .
- سپس، بسته به مقدار هش، Node در یک سلول خاص در آرایه جدول[] قرار می گیرد .
- اگر سلول جدول[] مورد استفاده برای ذخیره عنصر Node فعلی خالی نباشد، اما از قبل دارای یک عنصر باشد، عناصر Node روی مقدار بعدی تکرار می شوند تا به آخرین عنصر برسند. یعنی اونی که فیلد بعدیش تهی باشه .
در طول این جستجو، کلید شی Node محافظت شده با کلیدهای مورد جستجو مقایسه می شود:
- اگر مطابقت پیدا شود، جستجو به پایان می رسد و گره جدید گره ای را که مطابقت در آن پیدا شده است بازنویسی می کند (فقط فیلد مقدار آن بازنویسی می شود ).
- اگر هیچ منطبق کلیدی یافت نشد، گره جدید آخرین مورد در این لیست خواهد بود و گره قبلی پیوند بعدی به آن خواهد داشت.
10. تکرار کننده را توضیح دهید
در نمودار نگاشت سلسله مراتب مجموعه در بالا، رابط Collection جایی بود که کل سلسله مراتب شروع شد، اما در عمل کاملاً اینطور نیست. مجموعه از یک اینترفیس با متد iterator() به ارث می برد ، که یک شی را برمی گرداند که رابط Iterator<E> را پیاده سازی می کند . رابط Iterator به شکل زیر است:public interface Iterator <E>{
E next();
boolean hasNext();
void remove();
}
next() - با فراخوانی این متد، می توانید عنصر بعدی را دریافت کنید. hasNext() - به شما امکان می دهد بفهمید که آیا عنصر بعدی وجود دارد یا خیر و آیا به پایان مجموعه رسیده است یا خیر. و هنگامی که هنوز عناصر وجود دارد، hasNext() true را برمی گرداند . معمولاً hasNext() قبل از متد ()next فراخوانی می شود ، زیرا next() یک NoSuchElementException را زمانی که به انتهای مجموعه می رسد پرتاب می کند . remove() - عنصری را که با آخرین فراخوانی به next() بازیابی شده بود حذف می کند . هدف Iterator تکرار روی عناصر است. مثلا:
Set<Integer> values = new TreeSet<>();
values.add(5);
values.add(3);
values.add(6);
values.add(8);
values.add(2);
values.add(4);
values.add(1);
values.add(7);
Iterator<Integer> iter = values.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
در واقع، حلقه for-هر در زیر هود با استفاده از یک تکرار کننده پیاده سازی می شود. شما می توانید بیشتر در مورد این در اینجا بخوانید . List نسخه مخصوص به خود را از یک تکرار کننده ارائه می دهد، اما یک نسخه سردتر و پیچیده تر - ListIterator . این رابط Iterator را گسترش می دهد و روش های اضافی دارد:
- اگر عنصر قبلی در مجموعه وجود داشته باشد، hasPrevious درست است، در غیر این صورت false.
- previous عنصر فعلی را برمی گرداند و به عنصر قبلی منتقل می شود. اگر هیچ کدام وجود نداشته باشد، یک NoSuchElementException پرتاب می شود.
- add شیء ارسال شده را قبل از عنصر وارد می کند تا با فراخوانی بعدی به next() برگردانده شود .
- set به عنصر فعلی یک مرجع به شیء ارسال شده اختصاص می دهد.
- nextIndex شاخص عنصر بعدی را برمی گرداند. اگر چنین چیزی وجود نداشته باشد، اندازه لیست برگردانده می شود.
- previousIndex شاخص عنصر قبلی را برمی گرداند. اگر وجود نداشته باشد، عدد -1 برگردانده می شود.
GO TO FULL VERSION