כיצד להעתיק מערך ב- Java

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

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

2. ה מערכת מעמד

נתחיל בספריית הליבה של Java - System.arrayCopy (); זה מעתיק מערך ממערך מקור למערך יעד, ומתחיל את פעולת ההעתקה ממיקום המקור למיקום היעד עד לאורך שצוין.

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

אם אחד מטיעוני המערך הוא ריק, זה זורק א NullPointerException ואם אחד מהטיעונים השלמים הוא שלילי או מחוץ לטווח, הוא זורק IndexOutOfBoundException.

בואו נסתכל על דוגמה להעתקת מערך מלא לאחר באמצעות ה- java.util.System מעמד:

int [] מערך = {23, 43, 55}; int [] copiedArray = int int [3]; System.arraycopy (מערך, 0, copiedArray, 0, 3);

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

בואו נסתכל על דוגמה אחרת המציגה העתקת תת רצף ממערך מקור ליעד:

int [] מערך = {23, 43, 55, 12, 65, 88, 92}; int [] copiedArray = int int [3]; System.arraycopy (מערך, 2, copiedArray, 0, 3); 
assertTrue (3 == copiedArray.length); assertTrue (copiedArray [0] == מערך [2]); assertTrue (copiedArray [1] == מערך [3]); assertTrue (copiedArray [2] == מערך [4]); 

3. ה מערכים מעמד

ה מערכים class מציע גם מספר שיטות עמוסות להעתקת מערך למשנהו. באופן פנימי, היא משתמשת באותה גישה המסופקת על ידי מערכת כיתה שראינו קודם. זה מספק בעיקר שתי שיטות, העתק של(…) ו copyRangeOf (...).

בואו נסתכל על העתק של ראשון:

int [] מערך = {23, 43, 55, 12}; int newLength = array.length; int [] copiedArray = Arrays.copyOf (מערך, newLength); 

חשוב לציין זאת מערכים שימושים בכיתה Math.min (...) לבחירת המינימום של אורך מערך המקור והערך של פרמטר האורך החדש לקביעת גודל המערך שהתקבל.

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

int [] מערך = {23, 43, 55, 12, 65, 88, 92}; int [] copiedArray = Arrays.copyOfRange (מערך, 1, 4); 
assertTrue (3 == copiedArray.length); assertTrue (copiedArray [0] == מערך [1]); assertTrue (copiedArray [1] == מערך [2]); assertTrue (copiedArray [2] == מערך [3]);

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

עובד [] copiedArray = Arrays.copyOf (עובדים, עובדים.אורך); עובדים [0] .setName (עובדים [0] .getName () + "_Changed"); assertArrayEquals (copiedArray, array);

מכיוון שהתוצאה היא עותק רדוד - שינוי בשם העובד של אלמנט במערך המקורי גרם לשינוי במערך ההעתקות.

וכך - אם אנחנו רוצים לעשות העתק עמוק מסוגים לא פרימיטיביים - נוכל ללכת על האפשרויות האחרות המתוארות בסעיפים הקרובים.

4. העתק מערך עם Object.clone ()

Object.clone () עוברת בירושה מ לְהִתְנַגֵד כיתה במערך.

בואו נעתיק תחילה מערך של סוגים פרימיטיביים בשיטת שיבוט:

int [] מערך = {23, 43, 55, 12}; int [] copiedArray = array.clone (); 

והוכחה שזה עובד:

assertArrayEquals (copiedArray, array); מערך [0] = 9; assertTrue (copiedArray [0]! = מערך [0]);

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

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

זה יוצר עותק רדוד של רכיבי מערך הסוג הלא פרימיטיבי, גם אם המחלקה של האובייקט הסגור מיישמת את ניתנת לשיבוט ממשק ועוקף את שיבוט () שיטה מה- לְהִתְנַגֵד מעמד.

בואו נסתכל על דוגמה:

class class כתובת מיישמת Cloneable {// ... @ Override מוגן שיבוט אובייקט () זורק CloneNotSupportedException {super.clone (); כתובת כתובת = כתובת חדשה (); address.setCity (this.city); כתובת חזרה; }} 

אנו יכולים לבדוק את היישום שלנו על ידי יצירת מערך כתובות חדש והפעלת שלנו שיבוט () שיטה:

כתובת [] כתובות = createAddressArray (); כתובת [] copiedArray = addresses.clone (); כתובות [0] .setCity (כתובות [0] .getCity () + "_Changed"); 
assertArrayEquals (copiedArray, כתובות);

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

5. שימוש ב- זרם ממשק API

מתברר שאנחנו יכולים להשתמש ב- API של Stream גם להעתקת מערכים. בואו נסתכל על דוגמה:

מחרוזת [] strArray = {"כתום", "אדום", "ירוק" "}; String [] copiedArray = Arrays.stream (strArray) .toArray (String [] :: new); 

עבור הסוגים הלא פרימיטיביים, הוא גם יעשה עותק רדוד של אובייקטים. ללמוד עוד על ג'אווה 8 זרמיםאתה יכול להתחיל כאן.

6. ספריות חיצוניות

אפאצ'י קומונס 3 מציע מחלקת שירות הנקראת SerializationUtils המספק א שיבוט (...) שיטה. זה מאוד שימושי אם עלינו לעשות העתק עמוק של מערך סוגים לא פרימיטיביים. ניתן להוריד אותו מכאן ותלותו ב- Maven היא:

 org.apache.commons commons-lang3 3.5 

בואו נסתכל על מקרה מבחן:

מחלקה ציבורית מיישמת עובדים ניתן להתבצע באמצעות סידורי {// שדות // גטרים סטנדרטיים וקובעים} עובדים [] עובדים = createEmployeesArray (); עובד [] copiedArray = SerializationUtils.clone (עובדים); 
עובדים [0] .setName (עובדים [0] .getName () + "_Changed"); assertFalse (copiedArray [0] .getName (). שווה (עובדים [0] .getName ()));

מחלקה זו מחייבת שכל אובייקט ישמש את ה- ניתן לבצע סדרתי מִמְשָׁק. מבחינת ביצועים, זה איטי יותר משיטות השיבוט שנכתבו ידנית לכל אחד מהאובייקטים בגרף האובייקטים שלנו להעתקה.

7. מסקנה

במדריך זה, הסתכלנו על האפשרויות השונות להעתקת מערך ב- Java.

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

עבור סוגים שאינם פרימיטיביים, אם נצטרך לעשות עותק עמוק של מערך נוכל להשתמש ב- SerializationUtils או הוסף שיטות שיבוט לשיעורים שלנו במפורש.

וכמו תמיד, הדוגמאות המוצגות במאמר זה זמינות באתר GitHub.