ลบ Integer ออกจากรายการ <Integer> อย่างถูกต้อง


201

นี่เป็นข้อผิดพลาดที่ดีที่ฉันเพิ่งพบ พิจารณารายการจำนวนเต็ม:

List<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(6);
list.add(7);
list.add(1);

มีการเดาอะไรเกี่ยวกับสิ่งที่เกิดขึ้นเมื่อคุณดำเนินการlist.remove(1)? เกี่ยวกับlist.remove(new Integer(1))อะไร สิ่งนี้อาจทำให้เกิดข้อบกพร่องที่น่ารังเกียจ

อะไรคือวิธีที่เหมาะสมในการแยกความแตกต่างระหว่างremove(int index)ซึ่งเอาองค์ประกอบจากดัชนีที่กำหนดและremove(Object o)ที่เอาองค์ประกอบโดยอ้างอิงเมื่อจัดการกับรายการของจำนวนเต็ม?


ประเด็นหลักที่ต้องพิจารณาที่นี่คือหนึ่งใน@Nikita ที่กล่าวถึง - การจับคู่พารามิเตอร์ที่แน่นอนจะมีความสำคัญเหนือกว่ามวยอัตโนมัติ


11
A: ปัญหาที่แท้จริงที่นี่เป็นคนที่ดวงอาทิตย์อย่างใดคิดว่ามี (ไม่เปลี่ยนรูป) เรียนห่อหุ้มรอบ primitives เป็นสมาร์ทและต่อมาเมื่อมีคนคิดว่าการมีอัตโนมัติ (UN) มวยได้แม้แต่อย่างชาญฉลาด ... และคนที่ให้ใช้ APIs กะพร่องกะแพร่งเริ่มต้น เมื่อลูกที่ดีกว่า สำหรับจุดประสงค์มากมายมีวิธีแก้ปัญหาที่ดีกว่าArraylist <Integer>ใหม่ ยกตัวอย่างเช่น Trove ให้สิ่งTIntArrayList ยิ่งฉันโปรแกรมใน Java (SCJP มาตั้งแต่ปี 2001) ยิ่งฉันใช้คลาส wrapper น้อยลงและยิ่งใช้ API ที่ออกแบบมาอย่างดี (Trove, Google เป็นต้น)
ไวยากรณ์ T3rr0r

คำตอบ:


230

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

รายการอินเตอร์เฟสระบุวิธีการลบสองวิธี (โปรดสังเกตการตั้งชื่อของอาร์กิวเมนต์):

  • remove(Object o)
  • remove(int index)

ซึ่งหมายความว่าlist.remove(1)จะลบวัตถุที่ตำแหน่ง 1 และremove(new Integer(1))ลบการเกิดขึ้นครั้งแรกขององค์ประกอบที่ระบุจากรายการนี้


110
หยิบนิด A: คือการปฏิบัติที่ดีกว่าInteger.valueOf(1) new Integer(1)วิธีการคงที่สามารถทำการแคชและดังนั้นคุณจะได้รับประสิทธิภาพที่ดีขึ้น
decitrig

ข้อเสนอของ Peter Lawrey ดีกว่าและหลีกเลี่ยงการสร้างวัตถุที่ไม่จำเป็น
assylias

@assylias: ข้อเสนอของ Peter Lawrey ทำในสิ่งเดียวกันกับข้อเสนอของ decitrig แต่มีความโปร่งใสน้อยกว่า
Mark Peters

@ MarkPeters ความคิดเห็นของฉันเกี่ยวกับnew Integer(1)แต่ฉันเห็นด้วยInteger.valueOf(1)หรือ(Integer) 1เทียบเท่า
assylias

68

คุณสามารถใช้การหล่อ

list.remove((int) n);

และ

list.remove((Integer) n);

ไม่สำคัญว่าถ้า n เป็น int หรือ Integer วิธีการนี้จะเรียกสิ่งที่คุณคาดหวังเสมอ

การใช้(Integer) nหรือInteger.valueOf(n)มีประสิทธิภาพมากกว่าnew Integer(n)ที่สองคนแรกสามารถใช้แคชจำนวนเต็มในขณะที่ต่อมามักจะสร้างวัตถุ


2
มันคงจะดีถ้าคุณสามารถอธิบายได้ว่าทำไมถึงเป็นเช่นนี้ :) [เงื่อนไขการล็อกอัตโนมัติ ... ]
Yuval Adam

โดยการใช้แคสติ้งคุณมั่นใจได้ว่าคอมไพเลอร์เห็นประเภทที่คุณคาดหวัง ในกรณีแรก '(int) n' สามารถเป็นประเภทint ได้ในกรณีที่สอง '(จำนวนเต็ม) n' สามารถเป็นประเภทจำนวนเต็มเท่านั้น 'n' จะถูกแปลง / กล่อง / ไม่ได้ทำตามที่ต้องการหรือคุณจะได้รับข้อผิดพลาดของคอมไพเลอร์หากไม่สามารถทำได้
Peter Lawrey

10

ฉันไม่รู้วิธีที่ 'ถูกต้อง' แต่วิธีที่คุณแนะนำนั้นใช้ได้ดี:

list.remove(int_parameter);

เอาองค์ประกอบที่ตำแหน่งที่กำหนดและ

list.remove(Integer_parameter);

เอาวัตถุที่กำหนดออกจากรายการ

มันเป็นเพราะ VM ที่ความพยายามครั้งแรกที่จะหาวิธีการประกาศด้วยตรงชนิดพารามิเตอร์เดียวกันและเพียงแล้วพยายาม autoboxing


7

list.remove(4)เป็นการแข่งขันที่ถูกต้องของlist.remove(int index)ดังนั้นจึงจะถูกเรียก หากคุณต้องการโทรให้list.remove(Object)ทำดังนี้: list.remove((Integer)4).


ขอบคุณ Petar (Integer)นักแสดงที่เรียบง่ายอย่างที่คุณเขียนข้างต้นดูเหมือนจะเป็นวิธีที่ง่ายที่สุดสำหรับฉัน
vikingsteve

เมื่อใช้วิธีการล่าสุดของคุณดูเหมือนว่าจะส่งกลับบูลีน เมื่อพยายามสแต็กหลาย ๆ ครั้งฉันได้รับข้อผิดพลาดที่ไม่สามารถเรียก remove บนบูลีนได้
Bram Vanroy

4

มีการศึกษาเดาอะไรเกิดขึ้นเมื่อคุณรัน list.remove (1)? สิ่งที่เกี่ยวกับ list.remove (new Integer (1))

ไม่จำเป็นต้องเดา กรณีแรกจะส่งผลให้List.remove(int)ถูกเรียกและองค์ประกอบที่ตำแหน่ง1จะถูกลบออก กรณีที่สองจะส่งผลให้List.remove(Integer)ถูกเรียกและองค์ประกอบที่มีค่าเท่ากับInteger(1)จะถูกลบออก ในทั้งสองกรณีคอมไพเลอร์ Java เลือกโอเวอร์โหลดการจับคู่ที่ใกล้เคียงที่สุด

ใช่มีความเป็นไปได้ที่จะเกิดความสับสน (และข้อบกพร่อง) ที่นี่ แต่มันเป็นกรณีการใช้งานที่ค่อนข้างแปลก

เมื่อทั้งสองList.removeวิธีถูกกำหนดใน Java 1.2 การโอเวอร์โหลดไม่ชัดเจน ปัญหาเกิดขึ้นเฉพาะกับการแนะนำ generics และ autoboxing ใน Java 1.5 ในสายตาหลังจะดีกว่าถ้าหนึ่งในวิธีการเอาออกได้รับชื่ออื่น แต่มันสายเกินไปแล้ว


2

โปรดทราบว่าแม้ว่า VM ไม่ได้ทำสิ่งที่ถูกต้อง แต่คุณยังสามารถมั่นใจได้ถึงพฤติกรรมที่เหมาะสมโดยใช้ข้อเท็จจริงที่remove(java.lang.Object)ทำงานบนวัตถุที่กำหนดเอง:

myList.remove(new Object() {
  @Override
  public boolean equals(Object other) {
    int k = ((Integer) other).intValue();
    return k == 1;
  }
}

"การแก้ปัญหา" นี้แบ่งสัญญาของequalsวิธีการโดยเฉพาะ (จาก Javadoc) "มันสมมาตร: สำหรับค่าอ้างอิงที่ไม่เป็นโมฆะใด ๆ x และ y, x.equals (y) ควรกลับจริงถ้า y.equals ( x) ผลตอบแทนจริง ". ดังนั้นจึงไม่รับประกันว่าจะสามารถใช้งานได้กับทุกการใช้งานListเพราะการใช้งานใด ๆ ของรายการจะได้รับอนุญาตให้สลับ x และ y x.equals(y)ตามที่ต้องการเนื่องจาก Javadoc ของObject.equalsบอกว่าควรจะใช้งานได้
Erwin Bolwidt

1

เพียงฉันไม่ชอบดังต่อไปนี้ตามที่แนะนำโดย #decitrig ในคำตอบแรกที่ยอมรับความคิดเห็น

list.remove(Integer.valueOf(intereger_parameter));

สิ่งนี้ช่วยฉัน ขอบคุณอีกครั้ง #decitrig สำหรับความคิดเห็นของคุณ อาจช่วยได้บ้าง


0

นี่คือเคล็ดลับ

ลองมาสองตัวอย่างที่นี่:

public class ArrayListExample {

public static void main(String[] args) {
    Collection<Integer> collection = new ArrayList<>();
    List<Integer> arrayList = new ArrayList<>();

    collection.add(1);
    collection.add(2);
    collection.add(3);
    collection.add(null);
    collection.add(4);
    collection.add(null);
    System.out.println("Collection" + collection);

    arrayList.add(1);
    arrayList.add(2);
    arrayList.add(3);
    arrayList.add(null);
    arrayList.add(4);
    arrayList.add(null);
    System.out.println("ArrayList" + arrayList);

    collection.remove(3);
    arrayList.remove(3);
    System.out.println("");
    System.out.println("After Removal of '3' :");
    System.out.println("Collection" + collection);
    System.out.println("ArrayList" + arrayList);

    collection.remove(null);
    arrayList.remove(null);
    System.out.println("");
    System.out.println("After Removal of 'null': ");
    System.out.println("Collection" + collection);
    System.out.println("ArrayList" + arrayList);

  }

}

ตอนนี้เรามาดูผลลัพธ์:

Collection[1, 2, 3, null, 4, null]
ArrayList[1, 2, 3, null, 4, null]

After Removal of '3' :
Collection[1, 2, null, 4, null]
ArrayList[1, 2, 3, 4, null]

After Removal of 'null': 
Collection[1, 2, 4, null]
ArrayList[1, 2, 3, 4]

ตอนนี้เรามาวิเคราะห์ผลลัพธ์:

  1. เมื่อ 3 ถูกลบออกจากคอลเล็กชันมันจะเรียกremove()เมธอดของคอลเล็กชันที่รับObject oเป็นพารามิเตอร์ 3ดังนั้นมันจะเอาวัตถุ แต่ในวัตถุ arrayList มันจะถูกแทนที่โดยดัชนี 3 และด้วยเหตุนี้องค์ประกอบที่ 4 จะถูกลบออก

  2. ด้วยตรรกะเดียวกันของการลบออบเจ็กต์ null จะถูกลบในทั้งสองกรณีในเอาต์พุตที่สอง

ดังนั้นในการลบจำนวน3ซึ่งเป็นวัตถุที่เราอย่างชัดเจนจะต้องผ่าน 3 objectในฐานะที่เป็น

Integerและที่สามารถทำได้โดยการหล่อหรือห่อใช้คลาสเสื้อคลุม

เช่น:

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