การชกมวยและการแกะกล่องคืออะไรและการแลกเปลี่ยนคืออะไร?


136

ฉันกำลังมองหาคำตอบที่ชัดเจนกระชับและถูกต้อง

ตามหลักการแล้วเป็นคำตอบที่แท้จริงแม้ว่าจะยินดีต้อนรับลิงก์ไปยังคำอธิบายที่ดี


2
ไม่เข้าใจภาษานี้จริงๆหรือ?
Henk Holterman

4
@HenkHolterman ไม่ใช่ภาษาเฉพาะอย่างแน่นอนแม้ว่ามันจะไม่เกี่ยวข้องกับทุกภาษาก็ตาม- ความแตกต่างจะไม่เกี่ยวข้องกับภาษาที่พิมพ์แบบไดนามิกส่วนใหญ่เช่น ฉันไม่แน่ใจว่าจะใช้แท็กอะไรแทน - language-but-not-type-agnostic? static-language-agnosticเหรอ? ฉันไม่แน่ใจว่า SO ต้องการความแตกต่าง อาจเป็นคำถามที่ดีสำหรับเมตาว่า
Keith

คำตอบ:


192

ค่าแบบบรรจุกล่องคือโครงสร้างข้อมูลที่มีการห่อตัวน้อยที่สุดสำหรับประเภทดั้งเดิม * โดยทั่วไปค่าแบบบรรจุกล่องจะถูกเก็บไว้เป็นตัวชี้ไปยังวัตถุบนฮีปกอง

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

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

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

* สำหรับการสนทนานี้ค่าดั้งเดิมคือค่าใดก็ได้ที่สามารถเก็บไว้ได้ call stackแทนที่จะเก็บไว้เป็นตัวชี้ไปยังค่าบนฮีป บ่อยครั้งที่เป็นเพียงประเภทของเครื่องจักร (ints, float, ฯลฯ ) โครงสร้างและอาร์เรย์ขนาดคงที่ในบางครั้ง .NET-land เรียกพวกเขาว่าประเภทมูลค่า (ตรงข้ามกับประเภทอ้างอิง) ชาว Java เรียกพวกเขาว่าประเภทดั้งเดิม Haskellions เรียกพวกเขาว่าไม่มีกล่อง

** ฉันยังเน้นที่ Java, Haskell และ C # ในคำตอบนี้เพราะนั่นคือสิ่งที่ฉันรู้ สำหรับสิ่งที่คุ้มค่า Python, Ruby และ Javascript ล้วนมีค่าแบบบรรจุกล่องโดยเฉพาะ วิธีนี้เรียกอีกอย่างว่าแนวทาง "ทุกสิ่งคือวัตถุ" ***

*** ข้อแม้: ในบางกรณีคอมไพเลอร์ / JIT ขั้นสูงสามารถตรวจพบได้จริงว่าค่าที่อยู่ในกล่องตามความหมายเมื่อดูต้นทางสามารถเป็นค่าที่ไม่มีกล่องในขณะรันไทม์ได้อย่างปลอดภัย โดยพื้นฐานแล้วต้องขอบคุณผู้ใช้ภาษาที่ยอดเยี่ยมบางครั้งกล่องของคุณก็ฟรี


ทำไมถึงเป็นมูลค่าแบบบรรจุกล่อง CLR หรืออะไรก็ตามที่ได้รับจากมูลค่ามวย?
PositiveGuy

ในระยะสั้น (ฮ่า) พวกมันเป็นเพียง Object อื่นซึ่งสะดวกมาก Primitives (ใน Java เป็นอย่างน้อย) ไม่ได้สืบเชื้อสายมาจาก Object ไม่มีฟิลด์ไม่มีเมธอดและโดยทั่วไปจะทำงานแตกต่างจากค่าประเภทอื่น ๆ มาก ในทางกลับกันการทำงานกับพวกเขาอาจรวดเร็วและประหยัดพื้นที่ ดังนั้นการปิดการซื้อขาย
Peter Burns

2
Javascript เรียกว่าอาร์เรย์พิมพ์ (UInt32Array ใหม่ ฯลฯ ) ซึ่งเป็นอาร์เรย์ของ ints ที่ไม่มีกล่องและการลอยตัว
nponeccop


72

Boxing & unboxing คือกระบวนการแปลงค่าดั้งเดิมเป็นคลาส Wrapper เชิงวัตถุ (การชกมวย) หรือการแปลงค่าจากคลาส Wrapper เชิงวัตถุกลับไปเป็นค่าดั้งเดิม (unboxing)

ตัวอย่างเช่นใน java คุณอาจต้องแปลงintค่าเป็นInteger(boxing) หากคุณต้องการจัดเก็บในรูปแบบCollectionเนื่องจากไม่สามารถจัดเก็บแบบดั้งเดิมในCollectionออบเจ็กต์ได้ แต่เมื่อคุณต้องการนำมันกลับมาCollectionคุณอาจต้องการรับค่าเป็นintและไม่ใช่Integerดังนั้นคุณจึงต้องแกะกล่องออก

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

ทุกวันนี้มีการพูดถึงกันมากที่สุดในบริบทของคุณลักษณะ "autoboxing / autounboxing" ของ Java (และภาษาอื่น ๆ ) นี่คือJava คำอธิบายที่เป็นศูนย์กลางของ autoboxing


23

ใน. Net:

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

อย่างไรก็ตามobjectเป็นคลาสและเก็บเนื้อหาไว้เป็นข้อมูลอ้างอิง

List<int> notBoxed = new List<int> { 1, 2, 3 };
int i = notBoxed[1]; // this is the actual value

List<object> boxed = new List<object> { 1, 2, 3 };
int j = (int) boxed[1]; // this is an object that can be 'unboxed' to an int

แม้ว่าทั้งสองรายการจะมีข้อมูลเดียวกัน แต่รายการที่สองจะมีขนาดใหญ่และช้ากว่า แต่ละค่าในรายการที่สองเป็นการอ้างอิงถึงobjectที่เก็บไฟล์int.

สิ่งนี้เรียกว่าบรรจุกล่องเนื่องจากintถูกห่อด้วยobject. เมื่อมันกลับมาintจะไม่มีกล่อง - แปลงกลับเป็นค่าของมัน

สำหรับประเภทค่า (เช่นทั้งหมด structs ) สิ่งนี้ช้าและอาจใช้พื้นที่มากขึ้น

สำหรับประเภทการอ้างอิง (เช่น all classes ) นี่เป็นปัญหาน้อยกว่ามากเนื่องจากจะถูกเก็บไว้เป็นข้อมูลอ้างอิงอยู่ดี

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

สิ่งนี้อาจสร้างความสับสนเมื่อจัดการกับประเภทมูลค่าบรรจุกล่อง:

int a = 7;
int b = 7;

if(a == b) // Evaluates to true, because a and b have the same value

object c = (object) 7;
object d = (object) 7;

if(c == d) // Evaluates to false, because c and d are different instances

มันง่ายที่จะแก้ไข:

if(c.Equals(d)) // Evaluates to true because it calls the underlying int's equals

if(((int) c) == ((int) d)) // Evaluates to true once the values are cast

อย่างไรก็ตามมันเป็นอีกสิ่งหนึ่งที่ต้องระวังเมื่อจัดการกับค่ากล่อง


1
ใน vb.net ความแตกต่างระหว่างความหมายของความเท่าเทียมนั้นชัดเจนกว่าObjectไม่ได้ใช้ตัวดำเนินการความเท่าเทียมกัน แต่สามารถเปรียบเทียบประเภทคลาสกับตัวIsดำเนินการได้ ตรงกันข้ามInt32สามารถนำมาใช้กับผู้ประกอบการเท่าเทียมกัน Isแต่ไม่ได้ ความแตกต่างนั้นทำให้ชัดเจนขึ้นมากว่ากำลังทำการเปรียบเทียบประเภทใด
supercat

4

Boxingคือขั้นตอนการแปลงประเภทมูลค่าเป็นประเภทอ้างอิง ในขณะที่Unboxingการแปลงประเภทการอ้างอิงเป็นประเภทค่า

EX: int i = 123;
    object o = i;// Boxing
    int j = (int)o;// UnBoxing

ชนิดราคา ได้แก่ : int, charและ,structures enumerationsประเภทอ้างอิง: Classes, interfaces, arrays, stringsและobjects


3

คอลเลกชันทั่วไป. NET FCL:

List<T>
Dictionary<TKey, UValue>
SortedDictionary<TKey, UValue>
Stack<T>
Queue<T>
LinkedList<T>

ได้รับการออกแบบมาเพื่อเอาชนะปัญหาด้านประสิทธิภาพของการชกมวยและการแกะกล่องในการใช้งานคอลเลกชันก่อน

สำหรับข้อมูลเพิ่มเติมโปรดดูบทที่ 16, CLR ผ่าน C # (ฉบับที่ 2)


1

การชกมวยและการแกะกล่องช่วยอำนวยความสะดวกให้กับประเภทมูลค่าที่จะถือว่าเป็นวัตถุ Boxing หมายถึงการแปลงค่าเป็นอินสแตนซ์ของประเภทการอ้างอิงอ็อบเจ็กต์ ตัวอย่างเช่นIntเป็นคลาสและintเป็นชนิดข้อมูล แปลงintไปIntเป็นยกตัวอย่างของมวยในขณะที่แปลงIntไปintเป็น unboxing แนวคิดนี้ช่วยในการรวบรวมขยะ Unboxing ในทางกลับกันแปลงประเภทวัตถุเป็นประเภทค่า

int i=123;
object o=(object)i; //Boxing

o=123;
i=(int)o; //Unboxing.

ใน javascript, ผลตอบแทนvar ii = 123; typeof ii ผลตอบแทนที่ ผลตอบแทน นี่คือจาวาสคริปต์ที่เทียบเท่ากับการชกมวย ค่า iiObj จะถูกแปลงเป็นตัวเลขดั้งเดิมโดยอัตโนมัติ (ไม่มีกล่อง) เพื่อคำนวณเลขคณิตและส่งคืนค่าที่ไม่มีกล่อง numbervar iiObj = new Number(123); typeof iiObjobjecttypeof ii + iiObjnumber
PatS

-2

เช่นเดียวกับสิ่งอื่น ๆ การทำกล่องอัตโนมัติอาจเป็นปัญหาได้หากไม่ใช้อย่างระมัดระวัง คลาสสิกคือการลงท้ายด้วย NullPointerException และไม่สามารถติดตามได้ แม้จะมีดีบักเกอร์ ลองสิ่งนี้:

public class TestAutoboxNPE
{
    public static void main(String[] args)
    {
        Integer i = null;

        // .. do some other stuff and forget to initialise i

        i = addOne(i);           // Whoa! NPE!
    }

    public static int addOne(int i)
    {
        return i + 1;
    }
}

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

อืมและถ้าฉันทำอะไรบางอย่างระหว่างใน try catch block คอมไพเลอร์จะบังคับให้ฉันเริ่มต้นด้วยบางสิ่ง นี่ไม่ใช่รหัสจริง - เป็นตัวอย่างว่าจะเกิดขึ้นได้อย่างไร
PEELY

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