ทำไมคนยังคงใช้ประเภทดั้งเดิมใน Java?


163

ตั้งแต่ Java 5 เรามีการชกมวย / การแยกประเภทดั้งเดิมเพื่อที่intจะห่อjava.lang.Integerและอื่น ๆ และอื่น ๆ

ฉันเห็นมากของโครงการ Java ใหม่เมื่อเร็ว ๆ นี้ (ที่แน่นอนต้องมี JRE อย่างน้อย 5 รุ่นหากไม่ได้ 6) ที่มีการใช้intมากกว่าjava.lang.Integerแม้ว่ามันจะสะดวกสบายมากขึ้นที่จะใช้หลังตามที่มีวิธีการช่วยเหลือไม่กี่สำหรับการแปลง ถึงlongค่าและอื่น ๆ

ทำไมบางชนิดยังคงใช้ชนิดดั้งเดิมใน Java มีประโยชน์ใด ๆ ที่เป็นรูปธรรม?


49
เคยคิดเกี่ยวกับการใช้หน่วยความจำและประสิทธิภาพหรือไม่
Tedil

76
ฉันเพิ่มแท็ก autoboxing ... และพบว่าคนสามคนติดตามมันจริง จริงๆ? ผู้คนติดตามแท็ก AUTOBOXING หรือไม่
corsiKa

4
@ glowcoder พวกเขาไม่ใช่คนจริงพวกเขาเป็นเพียงแนวคิดเชิงนามธรรมที่สมมติว่าเป็นรูปแบบของมนุษย์ที่จะตอบกลับเกี่ยวกับ SO :)
biziclop

9
@TK Kocheran ส่วนใหญ่เป็นเพราะnew IntegeR(5) == new Integer(5)กฎควรประเมินเป็นเท็จ
biziclop

10
ดู GNU Trove หรือ Mahout Collections หรือ HPPC หรือ ... สำหรับวิธีแก้ปัญหาการรวบรวมประเภทดั้งเดิม พวกเราที่สนใจเกี่ยวกับความเร็วใช้เวลาของเราโดยใช้ประเภทดั้งเดิมมากขึ้นไม่น้อย
bmargulies

คำตอบ:


395

ในJava ที่มีประสิทธิภาพของ Joshua Bloch รายการที่ 5: "หลีกเลี่ยงการสร้างวัตถุที่ไม่จำเป็น" เขาโพสต์ตัวอย่างโค้ดต่อไปนี้:

public static void main(String[] args) {
    Long sum = 0L; // uses Long, not long
    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
        sum += i;
    }
    System.out.println(sum);
}

และใช้เวลา 43 วินาทีในการเรียกใช้ การใช้ Long เข้าสู่ดึกดำบรรพ์นั้นจะลดลงเป็น 6.8 วินาที ... หากนั่นเป็นข้อบ่งชี้ว่าทำไมเราถึงใช้ primitives

ขาดความเสมอภาคค่าพื้นเมืองยังเป็นกังวล ( .equals()ค่อนข้าง verbose เมื่อเทียบกับ==)

สำหรับ biziclop:

class Biziclop {

    public static void main(String[] args) {
        System.out.println(new Integer(5) == new Integer(5));
        System.out.println(new Integer(500) == new Integer(500));

        System.out.println(Integer.valueOf(5) == Integer.valueOf(5));
        System.out.println(Integer.valueOf(500) == Integer.valueOf(500));
    }
}

ผลลัพธ์ใน:

false
false
true
false

แก้ไข ทำไม (3) ผลตอบแทนtrueและ (4) การกลับมาfalse?

เพราะมันเป็นวัตถุสองอย่างที่แตกต่างกัน จำนวนเต็ม 256 ที่ใกล้เคียงกับศูนย์ [-128; 127] ถูกแคชโดย JVM ดังนั้นพวกเขาจึงส่งคืนวัตถุเดียวกันสำหรับวัตถุเหล่านั้น นอกเหนือจากช่วงนั้นแม้ว่ามันจะไม่ถูกแคชดังนั้นจึงมีการสร้างวัตถุใหม่ เพื่อให้สิ่งต่าง ๆ มีความซับซ้อน JLS ต้องการให้มีน้ำหนักอย่างน้อย 256 fly แคช ผู้ดำเนินการ JVM อาจเพิ่มมากขึ้นหากพวกเขาต้องการซึ่งหมายความว่าสิ่งนี้สามารถทำงานบนระบบที่แคช 1024 ที่ใกล้ที่สุดและทั้งหมดกลับเป็นจริง ... #awkward


54
ลองจินตนาการว่าiได้รับการประกาศLongเช่นกัน!
ColinD

14
@TREE - ข้อมูลจำเพาะต้องการ VMs เพื่อสร้าง flyweights ภายในช่วงที่กำหนด แต่น่าเสียดายที่มันช่วยให้พวกเขาขยายขอบเขตนั้นซึ่งหมายความว่าโปรแกรมอาจทำงานแตกต่างกันไปบน VMs ที่แตกต่างกัน มากสำหรับแพลตฟอร์มข้าม ...
แดเนียล Earwicker

12
Java ลงไปหมดแล้วด้วยตัวเลือกการออกแบบที่แย่ลงเรื่อย ๆ การล็อกอัตโนมัติเป็นความล้มเหลวโดยสมบูรณ์ซึ่งไม่สามารถคาดเดาได้หรือพกพาได้ ฉันสงสัยจริงๆว่าพวกเขากำลังคิดอะไรอยู่ ... แทนที่จะแก้ไขความเป็นคู่ดั้งเดิมของวัตถุที่น่ากลัวที่พวกเขาจัดการเพื่อทำให้แย่ลงกว่าตอนแรก
Pop Catalin

34
@ แคทลินฉันไม่เห็นด้วยกับคุณที่ autoboxing เป็นความล้มเหลวอย่างสมบูรณ์ มีข้อบกพร่องบางอย่างซึ่งไม่แตกต่างจากการออกแบบอื่น ๆ ที่สามารถใช้งานได้ (รวมถึงไม่มีอะไร) พวกเขาทำให้ชัดเจนมากในสิ่งที่คุณสามารถและไม่สามารถคาดหวังได้และเช่นเดียวกับการออกแบบอื่น ๆ ที่พวกเขาคาดหวังว่านักพัฒนา ของการออกแบบเหล่านั้น
corsiKa

9
@NaftuliTzviKay นั่นไม่ใช่ "ความล้มเหลว" พวกเขาทำให้มันชัดเจนมากว่า==ผู้ประกอบการทำการเปรียบเทียบตัวตนของการอ้างอิงในIntegerการแสดงออกและการเปรียบเทียบความเท่าเทียมกันของมูลค่าในintการแสดงออก Integer.equals()มีอยู่ด้วยเหตุผลอย่างนี้ คุณไม่ควรใช้==เพื่อเปรียบเทียบค่าในประเภทที่ไม่ใช่ดั้งเดิม นี่คือ Java 101
NullUserException

86

การเปิดกล่องอัตโนมัติอาจนำไปสู่การพบ NPE ได้ยาก

Integer in = null;
...
...
int i = in; // NPE at runtime

ในสถานการณ์ส่วนใหญ่การกำหนดค่า Null ให้inชัดเจนน้อยกว่าด้านบน



40

ประเภทดั้งเดิม:

int x = 1000;
int y = 1000;

ตอนนี้ประเมิน:

x == y

trueมัน แปลกใจแทบจะไม่ ตอนนี้ลองใช้กล่องชนิด:

Integer x = 1000;
Integer y = 1000;

ตอนนี้ประเมิน:

x == y

falseมัน อาจ. ขึ้นอยู่กับรันไทม์ นั่นเป็นเหตุผลเพียงพอหรือไม่


36

นอกจากปัญหาด้านประสิทธิภาพและหน่วยความจำแล้วฉันอยากจะพบกับปัญหาอื่น: ส่วนListต่อประสานจะใช้งานไม่intได้
ปัญหาคือremove()วิธีโอเวอร์โหลด( remove(int)เทียบกับremove(Object)) remove(Integer)จะแก้ปัญหาในการเรียกหลังดังนั้นคุณไม่สามารถลบองค์ประกอบตามดัชนี

ในทางตรงกันข้ามมีข้อผิดพลาดเมื่อพยายามเพิ่มและลบint:

final int i = 42;
final List<Integer> list = new ArrayList<Integer>();
list.add(i); // add(Object)
list.remove(i); // remove(int) - Ouch!

7
มันจะถูกทำลายใช่ อย่างไรก็ตามการลบ (int) เป็นข้อบกพร่องของการออกแบบ IMO ไม่ควรใช้ชื่อเมธอดมากจนเกินไปหากมีโอกาสผสมกันน้อยที่สุด
MrBackend

4
@MrBackend ยุติธรรมเพียงพอ ที่น่าสนใจVectorมีremoveElementAt(int)จากจุดเริ่มต้น remove(int)ถูกนำมาใช้กับกรอบงานคอลเลกชันใน Java 1.2
xehpuk

6
@MrBackend: เมื่อมีการListออกแบบ API ไม่มี Generics หรือ Autoboxing อยู่ดังนั้นจึงไม่มีโอกาสมั่วremove(int)และremove(Object)...
Holger

@ Franklin Yu: แน่นอน แต่เมื่อออกแบบภาษา / เวอร์ชันใหม่โดยไม่มีข้อ จำกัด ด้านความเข้ากันได้คุณจะไม่หยุดที่จะเปลี่ยนการโอเวอร์โหลดที่โชคร้าย คุณเพียงแค่กำจัดความแตกต่างของดั้งเดิมและค่ากล่องรวมกันดังนั้นคำถามที่จะใช้จะไม่ปรากฏ
Holger

27

คุณลองจินตนาการถึง

  for (int i=0; i<10000; i++) {
      do something
  }

วนซ้ำด้วย java.lang.Integer แทนหรือไม่ java.lang.Integer ไม่สามารถเปลี่ยนรูปได้ดังนั้นการเพิ่มรอบแต่ละลูปจะสร้างวัตถุ Java ใหม่บนฮีปแทนที่จะเพิ่มการเพิ่ม int บนสแต็กด้วยคำสั่ง JVM เดียว การแสดงจะโหดเหี้ยม

ฉันไม่เห็นด้วยจริงๆว่าโหมดสะดวกในการใช้ java.lang.Integer มากกว่า int ในทางตรงกันข้าม. Autoboxing หมายความว่าคุณสามารถใช้ int ที่คุณจะต้องถูกบังคับให้ใช้ Integer และคอมไพเลอร์ java จะดูแลการแทรกโค้ดเพื่อสร้างออบเจ็กต์ Integer ใหม่สำหรับคุณ การ Autoboxing ทั้งหมดเกี่ยวกับการอนุญาตให้คุณใช้ int ที่คาดว่าจะมี Integer โดยคอมไพเลอร์จะแทรกการสร้างวัตถุที่เกี่ยวข้อง มันไม่มีทางที่จะลบหรือลดความต้องการ int ในตอนแรก ด้วยการล็อคอัตโนมัติคุณจะได้รับสิ่งที่ดีที่สุดทั้งสองโลก คุณจะได้รับจำนวนเต็มที่สร้างขึ้นสำหรับคุณโดยอัตโนมัติเมื่อคุณต้องการวัตถุจาวาแบบอิงฮีพและคุณจะได้รับความเร็วและประสิทธิภาพของอินเทอร์แอคทีฟเมื่อคุณทำการคำนวณทางคณิตศาสตร์และในท้องถิ่น


19

ประเภทดั้งเดิมนั้นเร็วกว่ามาก :

int i;
i++;

จำนวนเต็ม (ตัวเลขทั้งหมดและสตริง) เป็นชนิดไม่เปลี่ยนรูป : เมื่อสร้างแล้วจะไม่สามารถเปลี่ยนแปลงได้ ถ้าiเป็นจำนวนเต็มกว่าi++จะสร้างวัตถุจำนวนเต็มใหม่ - มีราคาแพงกว่ามากในแง่ของหน่วยความจำและโปรเซสเซอร์


คุณไม่ต้องการให้ตัวแปรหนึ่งเปลี่ยนหากคุณทำi++กับตัวแปรอื่นดังนั้น Integer ค่อนข้างจำเป็นต้องไม่เปลี่ยนรูปเพื่อให้สามารถทำสิ่งนี้ได้ (หรืออย่างน้อยi++ก็ต้องสร้างวัตถุจำนวนเต็มใหม่อยู่แล้ว) (และค่าดั้งเดิมนั้นไม่เปลี่ยนรูปแบบเช่นกัน - คุณไม่ได้สังเกตสิ่งนี้เพราะมันไม่ใช่วัตถุ)
Paŭlo Ebermann

4
@ Paŭlo: การบอกว่าคุณค่าดั้งเดิมนั้นไม่เปลี่ยนรูปแบบนั้นไร้ความหมาย เมื่อคุณกำหนดตัวแปรดั้งเดิมให้กับค่าใหม่คุณจะไม่สร้างอะไรใหม่ ไม่มีการจัดสรรหน่วยความจำที่เกี่ยวข้อง จุดของปีเตอร์ย่อมาจาก: i ++ สำหรับพื้นฐานไม่ได้จัดสรรหน่วยความจำ แต่สำหรับวัตถุที่จำเป็นต้องทำ
Eddie

@Eddie: (ไม่จำเป็นต้องจัดสรรหน่วยความจำก็สามารถคืนค่าแคชสำหรับบางค่าขนาดเล็กฉันคิดว่า.) จุดของฉันคือการเปลี่ยนแปลงจำนวนเต็มของ Integers ที่นี่ไม่ใช่จุดตัดสินใจคุณต้องการ มีวัตถุอื่นโดยไม่คำนึงถึงความไม่สามารถเปลี่ยนแปลงได้
Paŭlo Ebermann

@ Paŭlo: จุดเดียวของฉันคือ Integer เป็นลำดับความสำคัญช้าลงแล้วดั้งเดิม และนี่เป็นเพราะความจริงที่ว่ากล่องชนิดนั้นไม่เปลี่ยนรูปและทุกครั้งที่คุณเปลี่ยนค่าจะมีการสร้างวัตถุใหม่ ฉันไม่ได้อ้างว่ามีบางอย่างผิดปกติกับพวกเขาหรือความจริงที่ว่าพวกเขาไม่เปลี่ยนรูป เพียงแค่ว่าพวกเขาจะช้าลงและ coder ควรรู้ว่า ดูว่า Groovy อัตราค่าโดยสารยังไม่มีประเภทแบบดั้งเดิมได้อย่างไร jroller.com/rants/entry/why_is_groovy_so_slow
Peter Knego

1
การเปลี่ยนแปลงไม่ได้และ++เป็นปลาเฮอริ่งแดงที่นี่ ลองนึกภาพ Java ได้รับการปรับปรุงเพื่อรองรับการให้บริการการบรรทุกเกินพิกัดในวิธีที่ง่ายมากเช่นถ้าคลาส (เช่นIntegerมีวิธีการplusแล้วคุณสามารถเขียนi + 1แทนi.plus(1)และสมมติว่าคอมไพเลอร์ฉลาดพอที่จะขยายi++เข้าไปในi = i + 1ตอนนี้คุณสามารถพูดได้i++และมีประสิทธิภาพ "เพิ่มค่าตัวแปร i" โดยไม่ต้องIntegerเปลี่ยนแปลงได้
Daniel Earwicker

16

สิ่งแรกและสำคัญที่สุดคือนิสัย หากคุณเขียนโค้ดใน Java เป็นเวลาแปดปีคุณจะมีความเฉื่อยสะสมเป็นจำนวนมาก ทำไมต้องเปลี่ยนหากไม่มีเหตุผลที่น่าสนใจในการทำเช่นนั้น? มันไม่เหมือนกับการใช้งานกล่องแบบดั้งเดิมมาพร้อมกับข้อดีพิเศษใด ๆ

เหตุผลอื่นคือยืนยันว่าnullไม่ใช่ตัวเลือกที่ถูกต้อง มันจะไม่มีจุดหมายและทำให้เข้าใจผิดเพื่อประกาศผลรวมของตัวเลขสองตัวหรือตัวแปรลูปเป็นIntegerเป็น

มีแง่มุมด้านประสิทธิภาพของมันด้วยในขณะที่ความแตกต่างด้านประสิทธิภาพนั้นไม่สำคัญในหลาย ๆ กรณี (แม้ว่ามันจะเป็นเรื่องที่ค่อนข้างแย่) ไม่มีใครชอบเขียนโค้ดที่สามารถเขียนได้อย่างง่ายดายในวิธีที่เร็วกว่าที่เรามีอยู่แล้ว เคย.


15
ฉันไม่เห็นด้วย. ด้านประสิทธิภาพอาจมีความสำคัญ สิ่งนี้น้อยมากอาจเป็นแรงเฉื่อยหรือแรงของนิสัย
Eddie

7
@ Eddie อาจเป็นไปได้ แต่ไม่ค่อยมี เชื่อฉันเถอะสำหรับคนส่วนใหญ่ข้อโต้แย้งเรื่องประสิทธิภาพเป็นเพียงข้อแก้ตัว
biziclop

3
ฉันก็ต้องการที่จะปกป้องอาร์กิวเมนต์ประสิทธิภาพ บน Android ที่มี Dalvik แต่ละออบเจกต์ที่คุณสร้างจะเพิ่ม "ความเสี่ยง" ของ GC ที่ถูกเรียกใช้และออบเจ็กต์ที่คุณหยุดชั่วคราวจะมีความยาวมากขึ้น ดังนั้นการสร้าง Integers แทนที่จะเป็น int ในลูปอาจจะทำให้คุณต้องเสียเฟรมบางเฟรม
Igor Čordaš

1
@PSIXO มันเป็นจุดที่ยุติธรรมฉันเขียนมันด้วย Java ฝั่งเซิร์ฟเวอร์ล้วนๆ อุปกรณ์มือถือเป็นสัตว์ที่แตกต่างกันอย่างสิ้นเชิง แต่ประเด็นของฉันคือแม้แต่นักพัฒนาซอฟต์แวร์ที่เขียนโค้ดแย่ ๆ โดยไม่คำนึงถึงประสิทธิภาพจะอ้างเหตุผลนี้จากพวกเขาสิ่งนี้ฟังดูเป็นข้อแก้ตัว
biziclop

12

โดยวิธีการ Smalltalk มีเพียงวัตถุ (ไม่มีพื้นฐาน) และพวกเขาได้เพิ่มประสิทธิภาพจำนวนเต็มขนาดเล็กของพวกเขา (ใช้ไม่ทั้งหมด 32 บิตเพียง 27 หรือเช่นนั้น) เพื่อไม่จัดสรรพื้นที่กองใด ๆ แต่เพียงใช้รูปแบบบิตพิเศษ นอกจากนี้วัตถุทั่วไปอื่น ๆ (จริงเท็จเท็จ) มีรูปแบบบิตพิเศษที่นี่

ดังนั้นอย่างน้อยบน JVMs 64 บิต (ที่มีเนมสเปซชี้ 64 บิต) คุณควรไม่มีวัตถุใด ๆ ของ Integer, Character, Byte, Short, Boolean, Float (และ Long Long) เลย (นอกเหนือจากที่สร้างขึ้นเหล่านี้ โดยชัดแจ้งnew ...()) เฉพาะรูปแบบบิตพิเศษซึ่งสามารถจัดการโดยผู้ประกอบการปกติค่อนข้างมีประสิทธิภาพ


ฉันควรจะพูดว่า "การใช้งานบางอย่าง" เนื่องจากฉันไม่ได้อยู่ภายใต้ข้อกำหนดภาษาฉันคิดว่า (และน่าเศร้าที่ฉันไม่สามารถอ้างอิงแหล่งข้อมูลใด ๆ ที่นี่มันเป็นเพียงจากสิ่งที่ฉันได้ยินที่ไหนสักแห่ง)
Paŭlo Ebermann

ŭlo, JIT เก็บ meta ไว้ในตัวชี้แล้ว รวมถึงตัวชี้สามารถเก็บข้อมูล GC หรือ Klass (การเพิ่มประสิทธิภาพ Class เป็นความคิดที่ดีกว่าการปรับจำนวนเต็มให้เหมาะสมซึ่งฉันสามารถดูแลได้น้อยกว่า) การเปลี่ยนพอยน์เตอร์จะต้องใช้, เปลี่ยนรหัส / cmp / jnz (หรืออะไรทำนองนั้น) ก่อนที่ตัวชี้จะโหลด สาขาอาจจะไม่ได้รับการคาดการณ์ที่ดีจากฮาร์ดแวร์ (เนื่องจากเป็นทั้งประเภทค่าและวัตถุปกติ) และจะนำไปสู่ประสิทธิภาพในการทำงาน
bestsss

3
ฉันทำ Smalltalk มาหลายปีแล้ว การปรับให้เหมาะสมยังคงมีราคาค่อนข้างแพงสำหรับแต่ละการดำเนินการบนอินเทอร์เฟซที่พวกเขาต้องเปิดโปงและนำไปใช้ใหม่ ปัจจุบันจาวาอยู่ในระดับเดียวกับ C เมื่อจัดการกับตัวเลขดั้งเดิม หากเปิดหน้ากาก + มาสก์จะช้ากว่า> 30%
Moeller

9

ฉันไม่อยากเชื่อเลยว่าไม่มีใครพูดถึงสิ่งที่ฉันคิดว่าเป็นเหตุผลที่สำคัญที่สุด: "int" นั้นง่ายกว่าการพิมพ์มากกว่า "จำนวนเต็ม" ฉันคิดว่าคนดูถูกดูแคลนความสำคัญของไวยากรณ์ที่กระชับ ประสิทธิภาพไม่ใช่เหตุผลที่จะหลีกเลี่ยงเพราะส่วนใหญ่เมื่อมีการใช้ตัวเลขอยู่ในดัชนีลูปและการเพิ่มและเปรียบเทียบค่าใช้จ่ายเหล่านั้นในลูปที่ไม่ใช่เรื่องเล็ก (ไม่ว่าคุณจะใช้ int หรือ Integer)

อีกเหตุผลที่ได้รับคือคุณสามารถรับ NPE ได้ แต่ก็ง่ายที่จะหลีกเลี่ยงประเภทกล่อง (และรับประกันได้ว่าจะหลีกเลี่ยงได้ตราบใดที่คุณกำหนดค่าเริ่มต้นเป็นค่าที่ไม่เป็นศูนย์)

เหตุผลอื่นคือ (ใหม่ยาว (1,000)) == (ใหม่ยาว (1,000)) เป็นเท็จ แต่นั่นเป็นอีกวิธีหนึ่งในการพูดว่า ".equals" ไม่มีการสนับสนุนทางไวยากรณ์สำหรับชนิดบรรจุกล่อง (ไม่เหมือนกับตัวดำเนินการ <,> , =, ฯลฯ ) ดังนั้นเรากลับมาที่เหตุผล "เรียบง่ายกว่า"

ฉันคิดว่าตัวอย่างของการวนรอบดั้งเดิมของ Steve Yegge แสดงจุดของฉันได้ดีมาก http://sites.google.com/site/steveyegge2/language-trickery-and-ejb

คิดเกี่ยวกับสิ่งนี้: คุณใช้ประเภทฟังก์ชั่นในภาษาที่มีไวยากรณ์ที่ดีสำหรับพวกเขาบ่อยแค่ไหน (เช่นภาษาที่ใช้งานได้, python, ruby ​​และแม้กระทั่ง C) เปรียบเทียบกับ java ที่คุณต้องจำลองพวกมันด้วยอินเตอร์เฟสเช่น Runnable และ Callable and คลาสนิรนาม


8

สองเหตุผลที่จะไม่กำจัดดั้งเดิม:

  • ความเข้ากันได้ย้อนหลัง

ถ้ามันถูกกำจัดออกไปโปรแกรมเก่า ๆ จะไม่ทำงานเลย

  • JVM เขียนใหม่

JVM ทั้งหมดจะต้องถูกเขียนใหม่เพื่อสนับสนุนสิ่งใหม่นี้

  • หน่วยความจำขนาดใหญ่

คุณจะต้องเก็บค่าและการอ้างอิงซึ่งใช้หน่วยความจำมากขึ้น หากคุณมีไบต์จำนวนมากการใช้byteของจะน้อยกว่าการใช้Byteอย่างมาก

  • ปัญหาตัวชี้ Null

การประกาศint iสิ่งที่ทำแล้วiจะไม่มีปัญหา แต่การประกาศInteger iแล้วทำสิ่งเดียวกันจะทำให้เกิด NPE

  • ประเด็นความเท่าเทียมกัน

พิจารณารหัสนี้:

Integer i1 = 5;
Integer i2 = 5;

i1 == i2; // Currently would be false.

จะเป็นเท็จ ผู้ประกอบการจะต้องได้รับการโอเวอร์โหลดและนั่นจะส่งผลในการเขียนสิ่งที่สำคัญ

  • ช้า

การห่อหุ้มวัตถุนั้นช้ากว่าของดั้งเดิมอย่างมาก


i1 == i2; จะเป็นเท็จถ้า i1> = 128 เท่านั้นดังนั้นตัวอย่างปัจจุบันผิด
Geniy

7

วัตถุนั้นมีน้ำหนักมากกว่าประเภทดั้งเดิมดังนั้นประเภทดั้งเดิมนั้นมีประสิทธิภาพมากกว่าอินสแตนซ์ของคลาส wrapper

ประเภทดั้งเดิมนั้นง่ายมาก: ตัวอย่างเช่น int คือ 32 บิตและใช้หน่วยความจำ 32 บิตขึ้นไปและสามารถจัดการได้โดยตรง วัตถุจำนวนเต็มเป็นวัตถุสมบูรณ์ซึ่ง (เช่นวัตถุใด ๆ ) จะต้องถูกเก็บไว้ในกองและสามารถเข้าถึงได้ผ่านการอ้างอิง (ตัวชี้) ไปยังวัตถุ เป็นไปได้ว่าจะใช้หน่วยความจำมากกว่า 32 บิต (4 ไบต์)

ที่กล่าวว่าความจริงที่ว่า Java มีความแตกต่างระหว่างประเภทดั้งเดิมและที่ไม่ใช่ดั้งเดิมยังเป็นสัญญาณของอายุของภาษาการเขียนโปรแกรม Java ภาษาการเขียนโปรแกรมใหม่กว่าไม่มีความแตกต่างนี้ คอมไพเลอร์ของภาษาดังกล่าวฉลาดพอที่จะคิดออกเองถ้าคุณใช้ค่าง่ายหรือวัตถุที่ซับซ้อนมากขึ้น

ตัวอย่างเช่นใน Scala ไม่มีประเภทดั้งเดิม มีคลาส Int สำหรับจำนวนเต็มและ Int เป็นวัตถุจริง (ซึ่งคุณสามารถใช้วิธีการเป็นต้น) เมื่อคอมไพเลอร์รวบรวมรหัสของคุณมันจะใช้ ints ดั้งเดิมเบื้องหลังดังนั้นการใช้ Int ก็มีประสิทธิภาพเท่ากับการใช้ int ดั้งเดิมใน Java


1
ฉันคิดว่า JRE น่าจะ "ฉลาด" พอที่จะทำสิ่งนี้ได้ด้วยจาวาแบบดั้งเดิม ล้มเหลว.
Naftuli Kay

7

นอกจากสิ่งที่คนอื่นพูดแล้วตัวแปรดั้งเดิมดั้งเดิมไม่ได้รับการจัดสรรจากฮีป แต่จะอยู่ในสแต็กแทน แต่วัตถุจะถูกจัดสรรจากกองดังนั้นจึงต้องมีการรวบรวมขยะ


3
ขออภัยนี่ผิด JVM อัจฉริยะสามารถทำการวิเคราะห์แบบ escape ในการจัดสรรอ็อบเจ็กต์ใด ๆ และหากไม่สามารถหลบหนีได้ให้จัดสรรใน stack
rlibby

2
ใช่นี่เป็นจุดเริ่มต้นของคุณสมบัติของ JVM ที่ทันสมัย ในห้าปีสิ่งที่คุณพูดจะเป็นจริงสำหรับ JVM ส่วนใหญ่ที่ใช้งานอยู่ วันนี้มันไม่ใช่ ฉันเกือบแสดงความคิดเห็นเกี่ยวกับเรื่องนี้ แต่ตัดสินใจที่จะไม่แสดงความคิดเห็น บางทีฉันควรพูดอะไรบางอย่าง
Eddie

6

ประเภทดั้งเดิมมีข้อดีหลายประการ:

  • รหัสที่เรียบง่ายในการเขียน
  • ประสิทธิภาพดีขึ้นเนื่องจากคุณไม่ได้สร้างวัตถุสำหรับตัวแปร
  • เนื่องจากไม่ได้แสดงการอ้างอิงไปยังวัตถุจึงไม่จำเป็นต้องตรวจสอบ null
  • ใช้ประเภทดั้งเดิมเว้นแต่คุณจะต้องการใช้ประโยชน์จากคุณสมบัติการชกมวย

5

เป็นการยากที่จะทราบว่าการเพิ่มประสิทธิภาพประเภทใดเกิดขึ้นภายใต้ฝาครอบ

สำหรับการใช้งานในพื้นที่เมื่อคอมไพเลอร์มีข้อมูลเพียงพอที่จะทำการปรับให้เหมาะสมซึ่งไม่รวมถึงความเป็นไปได้ของค่า Null ผมคาดว่าประสิทธิภาพจะเหมือนหรือคล้ายกันผมคาดว่าผลการดำเนินงานจะเหมือนกันหรือคล้ายกัน

อย่างไรก็ตามอาเรย์ของดั้งเดิมนั้นแตกต่างจากคอลเล็กชั่นของกล่องดั้งเดิมอย่างมาก สิ่งนี้สมเหตุสมผลแล้วเนื่องจากการเพิ่มประสิทธิภาพน้อยมากที่อาจเกิดขึ้นได้ในคอลเลกชัน

นอกจากนี้Integerยังมีค่าใช้จ่ายเชิงตรรกะที่สูงขึ้นมากเมื่อเทียบกับint: ตอนนี้คุณต้องกังวลเกี่ยวกับหรือไม่int a = b + c;โยนข้อยกเว้น

ฉันจะใช้คำสั่งพื้นฐานมากที่สุดเท่าที่จะเป็นไปได้และพึ่งพาวิธีการของโรงงานและการล็อกอัตโนมัติเพื่อให้ฉันได้รับกล่องชนิดที่มีพลังความหมายมากขึ้นเมื่อพวกเขาต้องการ


5
int loops = 100000000;

long start = System.currentTimeMillis();
for (Long l = new Long(0); l<loops;l++) {
    //System.out.println("Long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around Long: "+ (System.currentTimeMillis()- start));

start = System.currentTimeMillis();
for (long l = 0; l<loops;l++) {
    //System.out.println("long: "+l);
}
System.out.println("Milliseconds taken to loop '"+loops+"' times around long: "+ (System.currentTimeMillis()- start));

มิลลิวินาทีใช้เวลาในการวนรอบ '100000000' รอบยาว: 468

มิลลิวินาทีใช้เวลาในการวนรอบ '100000000' รอบนาน: 31

ในบันทึกย่อด้านข้างฉันจะไม่รังเกียจที่จะเห็นสิ่งนี้พบว่ามันเป็นวิธีใน Java

Integer loop1 = new Integer(0);
for (loop1.lessThan(1000)) {
   ...
}

โดยที่ for for จะเพิ่มลูป 1 โดยอัตโนมัติจาก 0 ถึง 1,000 หรือ

Integer loop1 = new Integer(1000);
for (loop1.greaterThan(0)) {
   ...
}

โดยที่ for for จะลดขนาดลูป 1 โดยอัตโนมัติเป็น 0


2
  1. คุณต้องมีพื้นฐานสำหรับการดำเนินการทางคณิตศาสตร์
  2. ดึกดำบรรพ์ใช้หน่วยความจำน้อยลงตามคำตอบข้างบน

คุณควรถามว่าทำไมต้องเป็น Class / Object type

เหตุผลที่มีประเภทวัตถุคือทำให้ชีวิตของเราง่ายขึ้นเมื่อเราจัดการกับ Collections ไม่สามารถเพิ่ม Primitives ลงในรายการ / แผนที่ได้โดยตรง แต่คุณต้องเขียนคลาส wrapper Readymade Integer ประเภทของ Classes ช่วยให้คุณที่นี่และมันมีวิธีการอรรถประโยชน์มากมายเช่น Integer.pareseInt (str)


2

ฉันเห็นด้วยกับคำตอบก่อนหน้าการใช้วัตถุห่อหุ้มแบบดั้งเดิมอาจมีราคาแพง แต่ถ้าประสิทธิภาพไม่สำคัญในแอปพลิเคชันของคุณคุณหลีกเลี่ยงการล้นเมื่อใช้วัตถุ ตัวอย่างเช่น:

long bigNumber = Integer.MAX_VALUE + 2;

ค่าของbigNumber-2147483647 และคุณคาดหวังว่าจะเป็น 2147483649 มันเป็นข้อผิดพลาดในรหัสที่จะแก้ไขโดยการทำ:

long bigNumber = Integer.MAX_VALUE + 2l; // note that '2' is a long now (it is '2L').

และbigNumberจะเป็น 2147483649 บางครั้งข้อผิดพลาดเหล่านี้เป็นเรื่องง่ายที่จะพลาดและอาจนำไปสู่พฤติกรรมหรือช่องโหว่ที่ไม่รู้จัก (ดูCWE-190 )

หากคุณใช้วัตถุห่อหุ้มรหัสที่เทียบเท่าจะไม่รวบรวม

Long bigNumber = Integer.MAX_VALUE + 2; // Not compiling

ดังนั้นจึงง่ายต่อการหยุดปัญหาประเภทนี้โดยใช้วัตถุห่อหุ้มดั้งเดิม

คำถามของคุณได้รับคำตอบแล้วว่าฉันตอบเพื่อเพิ่มข้อมูลอีกเล็กน้อยที่ไม่ได้กล่าวถึงก่อนหน้านี้


1

เพราะ JAVA ดำเนินการทางคณิตศาสตร์ทั้งหมดในประเภทดั้งเดิม ลองพิจารณาตัวอย่างนี้:

public static int sumEven(List<Integer> li) {
    int sum = 0;
    for (Integer i: li)
        if (i % 2 == 0)
            sum += i;
        return sum;
}

ที่นี่การแจ้งเตือนและการดำเนินการ unary plus ไม่สามารถนำไปใช้กับประเภทจำนวนเต็ม (ข้อมูลอ้างอิง) คอมไพเลอร์ดำเนินการยกเลิกการทำกล่องและดำเนินการ

ดังนั้นตรวจสอบให้แน่ใจว่ามีการดำเนินการ autoboxing และ unboxing จำนวนเท่าใดในโปรแกรม Java เนื่องจากต้องใช้เวลาในการดำเนินการนี้

โดยทั่วไปจะเป็นการดีกว่าที่จะเก็บอาร์กิวเมนต์ชนิดอ้างอิงและผลลัพธ์ของชนิดดั้งเดิม


1

ชนิดดั้งเดิมเป็นได้เร็วขึ้นมากและต้องมากหน่วยความจำน้อยหน่วยความจำน้อยดังนั้นเราอาจต้องการใช้มันมากกว่า

ในทางกลับกันข้อกำหนดภาษา Java ปัจจุบันไม่อนุญาตให้ใช้ประเภทดั้งเดิมในประเภทพารามิเตอร์ (generics) ในคอลเลกชัน Java หรือ Reflection API

เมื่อแอปพลิเคชันของเราต้องการคอลเลกชันที่มีองค์ประกอบจำนวนมากเราควรพิจารณาใช้อาร์เรย์ที่มีประเภท "ประหยัด" มากที่สุดเท่าที่จะเป็นไปได้

* สำหรับข้อมูลรายละเอียดดูแหล่งที่มา: https://www.baeldung.com/java-primitives-vs-objects


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.