Smart-World Surf

יחידה 4: מערכים

הכרת מבנה הנתונים מערך וביצוע פעולות בסיסיות עליו.

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

מהו מערך?

מערך הוא מבנה נתונים ליניארי המאפשר לאחסן אוסף של אלמנטים מאותו טיפוס (לדוגמה, אוסף של מספרים שלמים, או אוסף של מחרוזות). המאפיינים העיקריים של מערך הם:

  • טיפוס אחיד: כל האלמנטים במערך חייבים להיות מאותו טיפוס.
  • גודל קבוע: מרגע שהמערך נוצר, גודלו אינו ניתן לשינוי.
  • זיכרון רציף: האלמנטים מאוחסנים בזיכרון אחד אחרי השני, ברצף.
  • גישה באמצעות אינדקס: לכל אלמנט במערך יש מספר סידורי (אינדקס) ייחודי, המתחיל מ-0.
מערך (Array): מבנה נתונים ליניארי המאחסן אוסף של אלמנטים מאותו טיפוס, בזיכרון רציף, ונגיש באמצעות אינדקס מספרי (מ-0 ועד אורך המערך פחות 1).

עבודה עם מערכים ב-Java

ב-Java, מערכים הם אובייקטים. כדי להשתמש במערך, יש לבצע הצהרה (declaration) ואתחול (initialization).

הצהרה (Declaration)

הכרזה על משתנה שיכול להכיל הפניה למערך. לדוגמה: int[] numbers;. הסימון [] מציין שמדובר במערך. ניתן גם לכתוב int numbers[]; אך הצורה הראשונה מומלצת.

אתחול (Initialization)

הקצאת זיכרון למערך והשמת ערכים. ישנן מספר דרכים:

  • הקצאה עם גודל: numbers = new int[10]; יוצר מערך של 10 מספרים שלמים, המאותחלים לערכי ברירת מחדל (0 עבור int, false עבור boolean, null עבור אובייקטים).
  • הקצאה עם ערכים ראשוניים: int[] numbers = {1, 2, 3, 4, 5}; יוצר מערך בגודל 5 ומאתחל אותו בערכים הנתונים.

גישה לאלמנטים (Accessing Elements)

גישה לאלמנטים מתבצעת באמצעות האינדקס שלהם. האינדקסים מתחילים מ-0. לדוגמה: numbers[0] = 5; ישים את הערך 5 באלמנט הראשון. int x = numbers[2]; יקרא את הערך מהאלמנט השלישי.

אורך המערך (Array Length)

ניתן לקבל את אורך המערך באמצעות המאפיין length. לדוגמה: int size = numbers.length;. שימו לב שזהו מאפיין (field), לא שיטה (method), ולכן אין סוגריים.

מעבר על איברי המערך (Iteration)

לולאות הן הדרך הנפוצה ביותר לעבור על כל איברי המערך:

  • לולאת for רגילה:
    for (int i = 0; i 
  • לולאת for-each (Enhanced for loop): מתאימה לקריאת ערכים בלבד.
    for (int number : numbers) {
        System.out.println(number);
    }

פעולות נפוצות ודגשים חשובים

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

  • חיפוש: חיפוש ליניארי הוא הדרך הפשוטה ביותר למצוא אלמנט במערך על ידי מעבר על כל האלמנטים.
  • העתקת מערכים: העתקה פשוטה באמצעות השמה (arr2 = arr1;) יוצרת כינוי (aliasing) – שני משתנים המצביעים על אותו אובייקט בזיכרון. כדי להעתיק את התוכן עצמו למערך חדש, יש ליצור מערך חדש ולהעתיק את האלמנטים אחד-אחד, או להשתמש בשיטות כמו System.arraycopy() או Arrays.copyOf().
  • מערכים כפרמטרים לשיטות: כאשר מעבירים מערך לשיטה, למעשה מועברת הפניה (reference) למערך. משמעות הדבר היא ששינויים שיתבצעו על המערך בתוך השיטה ישפיעו על המערך המקורי מחוץ לשיטה.
  • מערכים רב-ממדיים: Java תומכת במערכים של מערכים, המאפשרים ליצור מבנים דמויי מטריצה. לדוגמה, int[][] matrix = new int[3][4]; יוצר מטריצה של 3 שורות ו-4 עמודות.
חריגת גבולות המערך (ArrayIndexOutOfBoundsException): זוהי אחת הטעויות הנפוצות ביותר והקריטיות בעבודה עם מערכים. היא מתרחשת כאשר מנסים לגשת לאלמנט באינדקס שאינו חוקי (קטן מ-0 או גדול או שווה ל-length של המערך). שגיאה זו היא שגיאת זמן ריצה (runtime error) והיא תגרום לקריסת התוכנית. תמיד ודאו שהאינדקסים שלכם נמצאים בטווח 0 עד length-1.

יעילות ומגבלות

מערכים מציעים יעילות גבוהה עבור פעולות מסוימות, אך יש להם גם מגבלות:

  • גישה לאלמנט לפי אינדקס: פעולה זו היא יעילה ביותר, בסיבוכיות זמן של O(1) (זמן קבוע), מכיוון שניתן לחשב את מיקום האלמנט בזיכרון ישירות.
  • הוספה או מחיקה של אלמנטים: פעולות אלו פחות יעילות, במיוחד באמצע המערך, מכיוון שהן דורשות הזזה של כל האלמנטים שאחרי נקודת ההוספה/מחיקה. סיבוכיות זמן היא O(N) במקרה הגרוע (כאשר N הוא גודל המערך).
  • מגבלת הגודל הקבוע: זוהי המגבלה המשמעותית ביותר. אם נדרש לאחסן מספר משתנה של אלמנטים, יש צורך ליצור מערך חדש בגודל שונה ולהעתיק את התוכן, או להשתמש במבני נתונים דינמיים יותר כמו ArrayList (שנלמד בהמשך).

שאלות לדיון

  • מהם היתרונות והחסרונות העיקריים של שימוש במערכים בהשוואה למבני נתונים אחרים (שעוד נלמד בהמשך, כמו רשימות מקושרות)?
  • הסבר מדוע גישה לאלמנט במערך היא פעולה יעילה (O(1)) מבחינת סיבוכיות זמן.
  • תאר מצב שבו תעדיף להשתמש במערך דו-ממדי, ותן דוגמה קונקרטית.
  • כיצד ניתן "להגדיל" מערך ב-Java, בהתחשב בכך שגודלו קבוע? תאר את התהליך.

נקודות לתשובת מודל

  • יתרונות וחסרונות:
    • יתרונות: גישה מהירה O(1) לאלמנטים לפי אינדקס, ניצול זיכרון יעיל (רציף).
    • חסרונות: גודל קבוע (לא ניתן לשנות לאחר יצירה), הוספה/מחיקה באמצע דורשת הזזת אלמנטים (O(N)).
  • יעילות גישה (O(1)): בגלל אחסון בזיכרון רציף, המערכת יודעת את כתובת הבסיס של המערך ואת גודל כל אלמנט. כדי למצוא אלמנט באינדקס i, היא פשוט מחשבת כתובת בסיס + i * גודל אלמנט. זוהי פעולה מתמטית קבועה שאינה תלויה בגודל המערך.
  • מערך דו-ממדי: מצבים כמו ייצוג מטריצות, לוחות משחק (לדוגמה, שחמט 8x8), או תמונות (פיקסלים). דוגמה: לוח משחק שחמט: char[][] board = new char[8][8];.
  • "הגדלת" מערך: ב-Java, לא ניתן לשנות את גודלו של מערך קיים. "הגדלה" מתבצעת על ידי יצירת מערך חדש, גדול יותר, והעתקת כל האלמנטים מהמערך הישן למערך החדש. לאחר מכן, יש להפנות את המשתנה המקורי למערך החדש. ניתן לבצע זאת באמצעות לולאה, System.arraycopy() או Arrays.copyOf().
מצאתם טעות או שחסר משהו?