סינון אוסף Java לפי רשימה

1. סקירה כללית

סינון א אוסף על ידי א רשימה הוא תרחיש לוגיקה עסקית נפוצה. יש הרבה דרכים להשיג זאת. עם זאת, חלקם עשויים להוביל לפתרונות נמוכים יותר אם הם לא נעשים כראוי.

במדריך זה, נשווה כמה יישומי סינון ונדון ביתרונות ובחסרונות שלהם.

2. שימוש בא לכל אחד לוּלָאָה

נתחיל בתחביר הקלאסי ביותר, לכל לולאה.

לשם כך ולכל הדוגמאות האחרות במאמר זה, נשתמש בכיתה הבאה:

שכבה ציבורית עובד {עובד שלם פרטי מספר מספר; שם מחרוזת פרטי; מחלקת מספרים שלמים פרטית; // קונסטרוקטור סטנדרטי, גטרים וקובעים. }

נשתמש גם בשיטות הבאות לכל הדוגמאות, לשם הפשטות:

רשימת פרטי buildEmployeeList () {return Arrays.asList (עובד חדש (1, "מייק", 1), עובד חדש (2, "ג'ון", 1), עובד חדש (3, "מרי", 1), עובד חדש ( 4, "ג'ו", 2), עובד חדש (5, "ניקול", 2), עובד חדש (6, "אליס", 2), עובד חדש (7, "בוב", 3), עובד חדש (8, "סקרלט", 3)); } רשימת עובדים עובד רשימת פרטי () {return Arrays.asList ("אליס", "מייק", "בוב"); }

לדוגמא, נסנן את הרשימה הראשונה של עובדים מבוסס על הרשימה השנייה עם עוֹבֵד שמות כדי למצוא רק את עובדים עם השמות הספציפיים האלה.

עכשיו, בואו נראה את הגישה המסורתית - לגלוש בשתי הרשימות ומחפש התאמות:

@Test ציבורי בטל givenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingForEachLoop () {רשימה filteredList = ArrayList חדש (); רשימת originalList = buildEmployeeList (); שם nameFilter = עובדNameFilter (); עבור (עובד עובד: originalList) {עבור (שם מחרוזת: nameFilter) {if (עובד.גטנאם (). שווה (שם)) {filteredList.add (עובד); // לשבור; }}} assertThat (filteredList.size (), is (nameFilter.size ())); }

זהו תחביר פשוט, אך הוא די מילולי, ולמעשה די לא יעיל. במילים פשוטות, זה חוזר על ידי המוצר הקרטזיאני של שתי הסטים על מנת לקבל את תשובתנו.

אפילו להוסיף א לשבור יציאה מוקדמת עדיין תחזור על אותה הזמנה כמו מוצר קרטזי במקרה הממוצע.

אם נקרא לגודל רשימת העובדים n, לאחר מכן שם פילטר יהיה בסדר גודל גדול באותה מידה, נותן לנו an O (n2) מִיוּן.

3. שימוש בזרמים ו- רשימה # מכילה

כעת נשכלל מחדש את השיטה הקודמת על ידי שימוש ב- lambdas כדי לפשט את התחביר ולשפר את הקריאה. בואו גם להשתמש ב- רשימה # מכילה שיטה כמו פילטר למבדה:

@Test public void givenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingLambda () {List filteredList; רשימת originalList = buildEmployeeList (); שם nameFilter = עובדNameFilter (); filteredList = originalList.stream () .filter (עובד -> nameFilter.contains (employee.getName ())) .collect (Collectors.toList ()); assertThat (filteredList.size (), הוא [nameFilter.size ())); }

באמצעות ממשק API של זרם, הקריאות שופרה מאוד, אך הקוד שלנו נשאר לא יעיל כמו השיטה הקודמת שלנו מכיוון שהוא עדיין חוזר דרך המוצר הקרטזיאני באופן פנימי. לפיכך, יש לנו את אותו הדבר O (n2) מִיוּן.

4. שימוש בזרמים עם HashSet

כדי לשפר את הביצועים, עלינו להשתמש ב- HashSet # מכיל שיטה. שיטה זו שונה מ רשימה # מכילה כי זה מבצע א קוד חשיש בדיקה, ומספק לנו מספר פעולות בזמן קבוע:

@Test public void givenEmployeeList_andNameFilterList_thenObtainFilteredEmployeeList_usingLambdaAndHashSet () {List filteredList; רשימת originalList = buildEmployeeList (); הגדר nameFilterSet = עובד NameFilter (). זרם (). Collect (Collectors.toSet ()); filteredList = originalList.stream () .filter (עובד -> nameFilterSet.contains (עובד. getName ())) .collect (Collectors.toList ()); assertThat (filteredList.size (), הוא [nameFilterSet.size ())); }

על ידי שימוש ב HashSet, יעילות הקוד שלנו השתפרה מאוד, אך לא השפיעה על הקריאה. מאז HashSet # מכיל פועל בזמן קבוע, שיפרנו את הסיווג שלנו ל עַל).

5. מסקנה

במדריך מהיר זה למדנו כיצד לסנן א אוסף על ידי א רשימה של ערכים וחסרונות השימוש במה שנראה כמו השיטה הכי פשוטה.

עלינו תמיד לשקול יעילות מכיוון שהקוד שלנו עשוי להסתיים במערכי נתונים ענקיים, ולבעיות ביצוע יכולות להיות השלכות קטסטרופליות בסביבות כאלה.

כל הקוד המוצג במאמר זה זמין ב- GitHub.


$config[zx-auto] not found$config[zx-overlay] not found