CodeGym /בלוג Java /Random-HE /Java.lang.Integer Class
John Squirrels
רָמָה
San Francisco

Java.lang.Integer Class

פורסם בקבוצה
ניתן לחלק את סוגי הנתונים של Java באופן מותנה לשני בלוקים: פרימיטיבי והתייחסות (מחלקות). ישנם מספר סוגי נתונים פרימיטיביים ב-Java, כגון מספרים שלמים ( בייט , קצר , int , ארוך ), מספרי נקודה צפה ( צף , כפול ), סוג נתונים לוגי ( בוליאני ) וסוג נתוני תו ( char ). בטח כבר ידעת שלכל סוג נתונים פרימיטיבי יש מחלקת עטיפה משלו. סוג נתוני התייחסות ש"עוטף" או הופך את אחיו הקטן הפרימיטיבי באובייקט ג'אווה. מספר שלם הוא מחלקה עטיפה עבור האח הפרימיטיבי שלו בשם int. מספר שלם באנגלית פירושו מספר שלם. הם יכולים להיות חיוביים, שליליים או 0. למרבה הצער, מספר שלם ב-Java לא אומר שום מספר שלם. מספר שלם ב-java הוא מספר שלם שמתאים ל-32 סיביות. אם אתה רוצה מספר גדול יותר אתה מוזמן להשתמש במספרי Java Long . לרשותם עומדים 64 ביטים. אם יתמזל מזלך להזדקק למספר גדול עוד יותר, ג'אווה סיפקת אותך עם BigInteger .

עבודה עם מספר שלם

כמחלקת עטיפה, Integer מספקת שיטות שונות לעבודה עם int , כמו גם מספר שיטות להמרת int למחרוזת ו- String ל- int . למחלקה יש שני בנאים:
  • public Integer(int i) , כאשר i הוא ערך פרימיטיבי לאתחל. זה יוצר אובייקט שלם שמאוחל עם ערך int .

  • public Integer(String s) זורק NumberFormatException . כאן s הוא ייצוג מחרוזת של ערך int . בנאי זה יוצר אובייקט של מספר שלם שאותחל עם ערך int המסופק על ידי ייצוג מחרוזת .

יצירת אובייקט מספר שלם

ישנן אפשרויות שונות ליצירת אובייקטים שלמים . אחד הנפוצים ביותר בשימוש הוא הקל ביותר. הנה דוגמא:
Integer myInteger = 5;
האתחול של המשתנה Integer במקרה זה דומה לאתחול של משתנה int הפרימיטיבי . אגב, אתה יכול לאתחל משתנה אינטגר עם הערך של int . הנה דוגמא:
int myInt = 5;
Integer myInteger = myInt;
System.out.println(myInteger);
הפלט כאן הוא:
5
למעשה, כאן אנו יכולים לראות אריזה אוטומטית. כמו כן, אנו יכולים ליצור אובייקט של מספר שלם בדיוק כמו כל אובייקט אחר באמצעות בנאי ומילת מפתח חדשה :
Integer myInteger = new Integer(5);
אתה יכול לעשות עם המשתנה Integer אותו דבר כמו עם int (להוסיף, להחסיר, להכפיל, לחלק, להגדיל, להפחית). עם זאת, חשוב לזכור שמספר שלם הוא סוג נתוני התייחסות, ומשתנה מסוג זה יכול להיות null. במקרה זה, עדיף להימנע מפעולות כאלה.
Integer myInteger1  = null;
Integer myInteger2 = myInteger1 + 5;
כאן נקבל חריג:
חריג בשרשור "ראשי" java.lang.NullPointerException"

קבועי מחלקה שלמים

המחלקה Integer מספקת קבועים ושיטות שונות לעבודה עם מספרים שלמים. הנה הם:
  • SIZE פירושו מספר הסיביות במערכת המספרים הדו ספרתית שתפוסה על ידי הסוג int

  • BYTES הוא מספר הבתים במערכת המספרים הדו ספרתית שנכבשו על ידי סוג int

  • MAX_VALUE הוא הערך המקסימלי שסוג int יכול להחזיק

  • MIN_VALUE הוא הערך המינימלי שסוג int יכול להחזיק

  • TYPE מחזיר אובייקט מסוג Class מהסוג int

מחלקה שלמים השיטות השימושיות ביותר

עכשיו בואו ניקח הצצה לשיטות הנפוצות ביותר של המחלקה Integer . הפופולריים שבהם, אני מניח, הם שיטות להמרת מספר ממחרוזת , או להיפך.
  • static int parseInt(String s) שיטה זו ממירה את String ל- int . אם ההמרה אינה אפשרית, NumberFormatException תוזרק.

  • static int parseInt(String s, int radix) שיטה זו גם ממירה את הפרמטר s ל- int . הפרמטר radix מציין שמערכת המספרים s נכתבה במקור.

בנוסף ל- parseInt , יש גם שיטת valueOf דומה מאוד בכמה וריאציות. עם זאת, התוצאה של valueOf תהיה Integer , ו- parseInt יהיה int .
  • static Integer valueOf(int i) מחזיר מספר שלם שהערך שלו הוא i ;

  • סטטי Integer valueOf(String s) עובד כמו parseInt(String s) , אבל התוצאה תהיה Integer , לא int ;

  • static Integer valueOf(String s, int radix) עובד כמו parseInt(String s, int radix) , אבל התוצאה היא Intger , לא int .

האם יש בעיה עם מחלקה אינטגר? אה כן, יש…

אז ישנם שני סוגים של מספרים שלמים (שמתאימים ל-32 סיביות) ב-Java: int ו- Integer . כדי להבין את הפרטים של כל אחד מהם אנחנו צריכים לדעת את הדברים הבאים על מודל הזיכרון של JVM: כל מה שאתה מצהיר מאוחסן או ב-Stack Memory (JVM Stack ספציפי לכל Thread), או Heap Space. סוגים פרימיטיביים ( int , long , float , boolean , double , char , byte וכו') מאוחסנים בזיכרון Stack. כל האובייקטים והמערכים מאוחסנים ב-Heap Space. הפניות לאובייקטים ולמערכים הללו הדרושים לשיטות מאוחסנות ב-Stack. כך. למה אכפת לנו? ובכן, אתה מבין, סטאק קטן יותר מ-Heap (מחדל), אבל הרבה יותר מהיר להקצות ערכים ב-Stack, מאשר ב-Heap (מקצוען). נתחיל עם סוג פרימיטיבי int . זה תופס בדיוק 32 סיביות. זה 32/8=4 בתים. כי זה טיפוס פרימיטיבי. כעת, בוא נבחן מספר שלם . זהו אובייקט, עם תקורה נוספת ויישורים. השתמשתי ב-jol של ספרייה כדי למדוד את הגודל שלו:
public static void main(String[] args) {
 	System.out.println(ClassLayout.parseInstance(Integer.valueOf(1)).toPrintable());
}
והתברר שהוא תופס 16 בתים:
אובייקטים פנימיים של java.lang.Integer: OFF SZ TYPE DESCRIPTION VALUE 0 8 (כותרת אובייקט: סימן) 0x000000748c90e301 (hash: 0x748c90e3; גיל: 0) 8 4 (כותרת אובייקט: class) 0x000 instance 0x00e instance 0x00t 16 בתים
מה?! זה פי 4 יותר זיכרון! אבל אל לנו לעצור שם. כמפתחי Java אנחנו בדרך כלל לא מפסיקים להשתמש במספר שלם בודד. מה שאנחנו באמת רוצים זה להשתמש בהרבה מהם. כמו ברצף. לדוגמה, במערך. או רשימה. מערכים מאוחסנים בערימה, כמו רשימות. לכן, ההקצאה צריכה לקחת בערך אותו פרק זמן. ימין? אבל מה אם נצטרך להקצות יותר זיכרון? בואו נבדוק כמה מקום לוקח מערך של 1000 ערכי int פרימיטיביים:
public static void main(String[] args) {
    	int[] array = new int[1000];
    	for (int i = 0; i < 1000; i++) array[i] = i;                System.out.println(ClassLayout.parseInstance(array).toPrintable());
}
והתוצאה היא 4016 בתים:
OFF SZ TYPE DESCRIPTION VALUE 0 8 (כותרת אובייקט: סימן) 0x00000000000000001 (לא מוטה; גיל: 0) 8 4 (כותרת אובייקט: מחלקה) 0x00006c38 12 4 (אורך מערך) 1000 (יישור ב-06t מרווח 012pad 4) [I.<elements> לא רלוונטי גודל מופע: 4016 בתים הפסדי שטח: 4 בתים פנימיים + 0 בתים חיצוניים = 4 בתים סך הכל
בסדר, זה די הגיוני, בהתחשב ב-int יחיד לוקח 4 בתים. מה לגבי ArrayList<Integer> של 1000 מספרים שלמים ? בוא נראה:
public static void main(String[] args) {
	List<Integer> list = new ArrayList<>(1000);
	for (int i = 0; i < 1000; i++) list.add(i);
      System.out.println(GraphLayout.parseInstance(list).toFootprint());
}
והתוצאה היא 20040 בתים (שוב, פי 4 יותר!):
java.util.ArrayList@66d3c617d טביעת רגל: COUNT AVG SUM DESCRIPTION 1 4016 4016 [Ljava.lang.Object; 1000 16 16000 java.lang.Integer 1 24 24 java.util.ArrayList 1002 20040 (סה"כ)
אז, ArrayList<Integer> תופס פי 4 יותר שטח זיכרון. זה לא טוב. אבל עדיין, רשימות קלות יותר מכיוון שאנו יכולים להוסיף ולמחוק אלמנטים! הו ג'אווה... למה אתה צריך לארוז הכל?! אבל, אני שוכח, ג'אווה נהדרת, וגדולתה טמונה בשפע של ספריות קוד פתוח שאנו יכולים להשתמש בהן! Trove4j הוא אחד מהם. יש לו TIntArrayList אשר באופן פנימי יש נתוני int[] . בואו נמדוד את הגודל שלו:
public static void main(String[] args) {
	TIntList list = new TIntArrayList(1000);
	for (int i = 0; i < 1000; i++) list.add(i);
	System.out.println(GraphLayout.parseInstance(list).toFootprint());
}
והתוצאה היא 4040 בתים (כמעט זהה לזה רק int[] !):
gnu.trove.list.array.TIntArrayList@7440e464d טביעת רגל: COUNT AVG SUM DESCRIPTION 1 4016 4016 [I 1 24 24 gnu.trove.list.array.TIntArrayList 2 4040 (סה"כ)
אז, בסופו של דבר, נוכל לקבל את הטוב משני העולמות! רשימות של מספרים שלמים שלוקחים פי 4 פחות מקום. וזה לא כולל מופעים של מספר שלם . רק אינט ס. לנו מפתחי Java באמת אכפת מזיכרון... אבל אכפת לנו גם מביצועים. יש ספריית microbenchmarking נפלאה עם שם צנוע jmh שמאפשרת לנו למדוד ביצועים של קוד. ראשית, הבה נשווה את הביצועים של חישוב סכום של שני מספרים שלמים אקראיים, מסודרים או לא: התצורה של jmh היא כדלקמן:
benchmark {
	configurations {
    	main {
        	warmups = 5 // number of warmup iterations
        	iterations = 50 // number of iterations
        	iterationTime = 500 // time in seconds per iteration
        	iterationTimeUnit = "ns" // time unit for iterationTime
אמות המידה:
private static final Random random = new Random();

@Benchmark
public int testPrimitiveIntegersSum() {
	int a = random.nextInt();
	int b = random.nextInt();
	return a + b;
}

@Benchmark
public Integer testBoxedIntegersSum() {
	Integer a = random.nextInt();
	Integer b = random.nextInt();
	return a + b;
}
התוצאות:
main: test.SampleJavaBenchmark.testBoxedIntegersSum 5693337.344 ±(99.9%) 1198774.178 פעולות/ש [ממוצע] (מינימום, ממוצע, מקסימום) = (1092314.989, 56904337, 56904337), 8204337,8204,837, 8204, 824, 824, 824, 824, 8. 1583.144 CI (99.9%): [4494563.166, 6892111.522] (מניח התפלגות נורמלית) ראשי: test.SampleJavaBenchmark.testPrimitiveIntegersSum 15295010.959 ±(99.9%) 2555447.456 פעולות לשנייה [ממוצע] (דקה, ממוצע, מקסימום) = (4560097.0495, 0495, 0495, 0459, 0459, 0459, 0459, 0459, . 7), stdev = 5162130.283 CI (99.9%): [12739563.502, 17850458.415] (מניח התפלגות נורמלית)
אז, בממוצע, ההקצאה והסכום של אינטס פרימיטיבי מהיר יותר מפי שניים מזו של מספרים שלמים מקופסת. כעת, בואו נשווה ביצועים של יצירה וחישוב של סכום אוספים (או מערכים של 1000 אינטס של מספרים שלמים):
@Benchmark
public int testPrimitiveArray() {
	int[] array = new int[1000];
	for (int i = 0; i < 1000; i++) array[i] = i;
	int sum = 0;
	for (int x : array) sum += x;
	return sum;
}
11933.545 ops/s [Average]


@Benchmark
public int testBoxesArray() {
	Integer[] array = new Integer[1000];
	for (int i = 0; i < 1000; i++) array[i] = i;
	int sum = 0;
	for (int x : array) sum += x;
	return sum;
}
2733.312 ops/s [Average]


@Benchmark
public int testList() {
	List<Integer> list = new ArrayList<>(1000);
	for (int i = 0; i < 1000; i++) list.add(i);
	int sum = 0;
	for (int x : list) sum += x;
	return sum;
}
2086.379 ops/s [Average]


@Benchmark
public int testTroveIntList() {
	TIntList list = new TIntArrayList(1000);
	for (int i = 0; i < 1000; i++) list.add(i);
	int sum = 0;
	for (int i = 0; i < 1000; i++) sum += list.get(i);
	return sum;
}
5727.979 ops/s [Average]
התוצאות: מערך פרימיטיביים מהיר יותר מפי 4 ממערך של ערכי מסגרת ( מספר שלם ); כמעט פי שישה מהר יותר מ- ArrayList של ערכים מקופסת ( Ingeger s); ומהיר פי שניים מ- TIntArrayList (שלמעשה מקשט מערך של אינטס פרימיטיבי). לכן, אם אתה צריך מבנה נתונים כדי לאחסן אוסף של ערכים שלמים, והגודל שלו לא ישתנה, השתמש ב- int[] ; אם הגודל עומד להשתנות - אולי תרצה להשתמש בספריית tove4j עם TIntArrayList . והנה מגיע הסוף של החיבור שלי שבו אני מסביר את החסרונות של שימוש בסוג מספר שלם . יש כמה שיטות סטטיות מעניינות של Integer , שעלי לדבר עליהן לפני שאסיים. Public static Integer getInteger(מחרוזת nm, int val) לא עושה את מה שאפשר לחשוב, אלא מאחזר ערך Integer של מאפיין מערכת. Val הוא ברירת המחדל למקרה שמאפיין זה אינו מוגדר. public static String toBinaryString(int i) מחזירה מחרוזת עם ייצוג בינארי של מספר. ישנן שיטות לשליפה של ייצוגים מבוססי-16 ( toHexString ) ו-based-8 ( toOctalString ). יש שיטה לנתח מחרוזת לתוך int . גם אם המחרוזת היא ייצוג שאינו מבוסס על 10 רדיקסים. הנה כמה דוגמאות: Integer.parseInt("-FF", 16) מחזירה -255 Integer.parseInt("+42", 10) מחזירה 42 Integer.parseInt("1100110", 2) מחזירה 102
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION