เหตุใดฉันจึงได้รับ UnsupportedOperationException เมื่อพยายามลบองค์ประกอบออกจากรายการ


476

ฉันมีรหัสนี้:

public static String SelectRandomFromTemplate(String template,int count) {
   String[] split = template.split("|");
   List<String> list=Arrays.asList(split);
   Random r = new Random();
   while( list.size() > count ) {
      list.remove(r.nextInt(list.size()));
   }
   return StringUtils.join(list, ", ");
}

ฉันได้รับสิ่งนี้:

06-03 15:05:29.614: ERROR/AndroidRuntime(7737): java.lang.UnsupportedOperationException
06-03 15:05:29.614: ERROR/AndroidRuntime(7737):     at java.util.AbstractList.remove(AbstractList.java:645)

นี่จะเป็นวิธีที่ถูกต้องได้อย่างไร Java.15


ใช้ LinkedList
Lova Chittumuri

คำตอบ:


1006

มีปัญหาเล็กน้อยเกี่ยวกับรหัสของคุณ:

เมื่อArrays.asListส่งคืนรายการขนาดคงที่

จาก API:

Arrays.asList: ส่งคืนรายการขนาดคงที่ที่สนับสนุนโดยอาร์เรย์ที่ระบุ

คุณทำไม่addได้ คุณทำไม่removeได้ Listคุณไม่สามารถปรับเปลี่ยนโครงสร้าง

แก้ไข

สร้างซึ่งรองรับได้เร็วขึ้นLinkedListremove

List<String> list = new LinkedList<String>(Arrays.asList(split));

ในsplitการจด regex

จาก API:

String.split(String regex): แยกสตริงรอบนี้ตรงกับของที่ได้รับการแสดงออกปกติ

|เป็นตัวบ่งชี้ regex; ถ้าคุณต้องการที่จะแยกตัวอักษร|คุณต้องหนีมันไป\|ซึ่งเป็นสตริง Java "\\|"ที่แท้จริงคือ

การแก้ไข:

template.split("\\|")

อัลกอริทึมที่ดีกว่า

แทนที่จะเรียกหมายเลขremoveทีละครั้งด้วยดัชนีสุ่มการสร้างตัวเลขสุ่มที่เพียงพอในช่วงนั้นจะดีกว่าและจากนั้นสำรวจภายในListครั้งเดียวด้วย a listIterator()แล้วเรียกหาremove()ดัชนีที่เหมาะสม มีคำถามเกี่ยวกับ stackoverflow เกี่ยวกับวิธีสร้างตัวเลขสุ่ม แต่แตกต่างกันในช่วงที่กำหนด

O(N)ด้วยวิธีนี้อัลกอริทึมของคุณจะ


ขอบคุณฉันมีองค์ประกอบที่ จำกัด ในสตริง <10 เท่านั้นดังนั้นจะไม่เป็นปัญหาการเพิ่มประสิทธิภาพ
Pentium10

6
@Pentium: อีกอย่างหนึ่ง: คุณไม่ควรสร้างตัวอย่างใหม่Randomทุกครั้ง ทำให้มันเป็นstaticสนามและเมล็ดมันเพียงครั้งเดียว
polygenelubricants

6
LinkedList เร็วขึ้นจริงหรือ ทั้ง LinkedList และ ArrayList มีการลบ O (n) ที่นี่: \ มันเกือบจะดีกว่าเสมอที่จะใช้ ArrayList
gengkev

2
LinkedList vs ArrayList -> มีกราฟทดสอบประสิทธิภาพจาก Ryan LinkedList เร็วขึ้นในการลบ
torno

LinkedList เท่านั้นที่เร็วกว่าการลบเมื่อโหนดที่จะลบนั้นเป็นที่รู้จักกันแล้ว หากคุณพยายามที่จะลบองค์ประกอบรายการจะต้องถูกสำรวจโดยแต่ละองค์ประกอบจะถูกเปรียบเทียบจนกว่าจะพบองค์ประกอบที่ถูกต้อง หากคุณพยายามลบโดยใช้ดัชนีต้องทำการสำรวจเส้นทาง การสำรวจเส้นทางเหล่านี้มีราคาแพงมากและเป็นกรณีที่เลวร้ายที่สุดสำหรับการแคช CPU: มีการข้ามไปมาในหน่วยความจำอย่างไม่คาดคิด ดู: youtube.com/watch?v=YQs6IC-vgmo
Alexander - Reinstate Monica

143

อันนี้เผาฉันมาหลายครั้งแล้ว Arrays.asListสร้างรายการ unmodifiable จาก Javadoc: ส่งคืนรายการขนาดคงที่ซึ่งสนับสนุนโดยอาร์เรย์ที่ระบุ

สร้างรายการใหม่ด้วยเนื้อหาเดียวกัน:

newList.addAll(Arrays.asList(newArray));

สิ่งนี้จะสร้างขยะเพิ่มเติมเล็กน้อย แต่คุณจะสามารถกลายพันธุ์ได้


6
จุดเล็ก ๆ น้อย ๆ แต่คุณไม่ได้ "ห่อ" รายการต้นฉบับคุณกำลังสร้างรายการใหม่ที่สมบูรณ์ (ซึ่งเป็นสาเหตุที่มันใช้งานได้)
Jack Leow

ใช่ฉันใช้ Arrays.asList () ในกรณีทดสอบ JUnit ซึ่งเก็บไว้ในแผนที่ของฉัน เปลี่ยนรหัสของฉันเพื่อคัดลอกรายการที่ส่งผ่านเข้าไปใน ArrayList ของฉันเอง
cs94njw

โซลูชันของคุณไม่ทำงานในสถานการณ์ของฉัน แต่ขอบคุณสำหรับคำอธิบาย ความรู้ที่คุณให้นั้นนำไปสู่ทางออกของฉัน
Scott Biggs

54

อาจเป็นเพราะคุณกำลังทำงานกับwrapper ที่ไม่สามารถแก้ไขได้ได้

เปลี่ยนบรรทัดนี้:

List<String> list = Arrays.asList(split);

ถึงบรรทัดนี้:

List<String> list = new LinkedList<>(Arrays.asList(split));

5
Arrays.asList () ไม่ใช่ wrapper ที่ไม่สามารถแก้ไขได้
Dimitris Andreou

@polygenelubricants: ดูเหมือนว่าคุณผสมขึ้นและunmodifiable หมายถึง "แก้ไขได้ แต่ไม่มีโครงสร้าง" immutableunmodifiable
โรมัน

2
ฉันแค่พยายามสร้างunmodifiableListเสื้อคลุมและลองset; UnsupportedOperationExceptionมันจะพ่น ฉันค่อนข้างแน่ใจCollections.unmodifiable*จริงๆหมายถึงการเปลี่ยนไม่ได้เต็มรูปแบบไม่เพียง แต่โครงสร้าง
polygenelubricants

1
การอ่านความคิดเห็นเหล่านั้น 7 ปีต่อมาฉันอนุญาตให้ฉันระบุลิงค์นี้: stackoverflow.com/questions/8892350/…มีแนวโน้มที่จะแก้ไขความแตกต่างระหว่างที่ไม่เปลี่ยนรูปและไม่สามารถแก้ไขได้
Nathan Ripert

14

ฉันคิดว่าการแทนที่:

List<String> list = Arrays.asList(split);

กับ

List<String> list = new ArrayList<String>(Arrays.asList(split));

แก้ไขปัญหา


5

รายการที่ส่งคืนโดยArrays.asList()อาจไม่เปลี่ยนรูป คุณลองได้ไหม

List<String> list = new ArrayList(Arrays.asList(split));

1
เขากำลังลบ ArrayList ไม่ใช่โครงสร้างข้อมูลที่ดีที่สุดสำหรับการลบค่า LinkedList feets มากขึ้นสำหรับปัญหาของเขา
โรมัน

2
ผิดเกี่ยวกับ LinkedList เขากำลังเข้าถึงโดยดัชนีดังนั้น LinkedList จะใช้เวลามากในการค้นหาองค์ประกอบผ่านการทำซ้ำ ดูคำตอบของฉันสำหรับวิธีที่ดีกว่าโดยใช้ ArrayList
Dimitris Andreou

4

เพียงอ่าน JavaDoc สำหรับวิธีการ asList:

ส่งคืน {@ รายการรหัส} ของวัตถุในอาร์เรย์ที่ระบุ ขนาดของ {@code List} ไม่สามารถแก้ไขได้เช่นการเพิ่มและลบไม่ได้รับการสนับสนุน แต่สามารถตั้งค่าองค์ประกอบได้ การตั้งค่าองค์ประกอบจะแก้ไขอาเรย์พื้นฐาน

นี่คือจาก Java 6 แต่ดูเหมือนว่าจะเหมือนกันสำหรับ android java

แก้ไข

ชนิดของรายการผลลัพธ์คือArrays.ArrayListซึ่งเป็นคลาสส่วนตัวภายใน Arrays.class จวนพูดมันคืออะไร Arrays.asListแต่รายการมุมมองในอาร์เรย์ที่คุณได้ผ่านไป ด้วยผลลัพธ์: หากคุณเปลี่ยนอาร์เรย์รายการจะถูกเปลี่ยนเช่นกัน และเนื่องจากอาร์เรย์ไม่สามารถปรับขนาดได้การลบและเพิ่มการดำเนินการจึงต้องไม่ได้รับการสนับสนุน


4

Arrays.asList () ส่งคืนรายการที่ไม่อนุญาตการดำเนินการที่ส่งผลกระทบต่อขนาด (โปรดทราบว่านี่ไม่เหมือนกับ "unmodifiable")

คุณสามารถทำได้new ArrayList<String>(Arrays.asList(split));เพื่อสร้างสำเนาจริง แต่เห็นสิ่งที่คุณพยายามทำนี่คือคำแนะนำเพิ่มเติม (คุณมีO(n^2)อัลกอริทึมด้านล่าง)

คุณต้องการลบองค์ประกอบแบบสุ่มlist.size() - count(เรียกว่านี้k) จากรายการ เพียงแค่เลือกองค์ประกอบสุ่มจำนวนมากและสลับไปยังkตำแหน่งสุดท้ายของรายการจากนั้นลบช่วงทั้งหมดนั้น (เช่นใช้ subList () และ clear () ในนั้น) นั่นจะทำให้มันกลายเป็นO(n)อัลกอรึทึมและค่าเฉลี่ย( O(k)แม่นยำยิ่งขึ้น)

อัปเดต : ดังที่ระบุไว้ด้านล่างอัลกอริธึมนี้เหมาะสมถ้าองค์ประกอบไม่ได้จัดเรียงเช่นถ้ารายการแสดงถึงกระเป๋า หากในอีกทางหนึ่งรายการมีลำดับที่มีความหมายอัลกอริทึมนี้จะไม่เก็บไว้ (อัลกอริทึมของ polygenelubricants แทนจะ)

อัปเดต 2 : ดังนั้นเมื่อมองย้อนกลับอัลกอริทึมที่ดีกว่า (เชิงเส้นคงไว้ซึ่งคำสั่ง แต่ด้วยตัวเลขสุ่ม (O) n) จะเป็นดังนี้:

LinkedList<String> elements = ...; //to avoid the slow ArrayList.remove()
int k = elements.size() - count; //elements to select/delete
int remaining = elements.size(); //elements remaining to be iterated
for (Iterator i = elements.iterator(); k > 0 && i.hasNext(); remaining--) {
  i.next();
  if (random.nextInt(remaining) < k) {
     //or (random.nextDouble() < (double)k/remaining)
     i.remove();
     k--;
  }
}

1
+1 สำหรับอัลกอริทึม แต่ OP บอกว่ามันมีเพียง 10 องค์ประกอบ ArrayListและวิธีที่ดีของการใช้ตัวเลขสุ่มด้วย ง่ายกว่าคำแนะนำของฉันมาก ฉันคิดว่ามันจะส่งผลในการเรียงลำดับขององค์ประกอบแม้ว่า
polygenelubricants

4

ฉันมีวิธีแก้ไขปัญหาอื่นอีก:

List<String> list = Arrays.asList(split);
List<String> newList = new ArrayList<>(list);

ทำงานบนnewList;)


2

UnsupportedOperationException นี้มาเมื่อคุณพยายามที่จะดำเนินการบางอย่างเกี่ยวกับคอลเลกชันที่ไม่ได้รับอนุญาตและในกรณีของคุณเมื่อคุณเรียกมันไม่ได้กลับArrays.asList java.util.ArrayListส่งคืนjava.util.Arrays$ArrayListซึ่งเป็นรายการที่ไม่เปลี่ยนรูป คุณไม่สามารถเพิ่มมันและคุณไม่สามารถลบออกได้


2

ใช่เปิดArrays.asListแล้วส่งคืนรายการขนาดคงที่

นอกเหนือจากการใช้รายการที่เชื่อมโยงเพียงใช้addAllรายการวิธีการ

ตัวอย่าง:

String idList = "123,222,333,444";

List<String> parentRecepeIdList = new ArrayList<String>();

parentRecepeIdList.addAll(Arrays.asList(idList.split(","))); 

parentRecepeIdList.add("555");

2

แทนที่

List<String> list=Arrays.asList(split);

ถึง

List<String> list = New ArrayList<>();
list.addAll(Arrays.asList(split));

หรือ

List<String> list = new ArrayList<>(Arrays.asList(split));

หรือ

List<String> list = new ArrayList<String>(Arrays.asList(split));

หรือ (ดีกว่าสำหรับองค์ประกอบลบ)

List<String> list = new LinkedList<>(Arrays.asList(split));

2

Arraylist narraylist = Arrays.asList (); // คืนค่าอาเรย์ลิสต์ที่ไม่เปลี่ยนรูปแบบเพื่อให้วิธีการแก้ไขที่ไม่แน่นอนจะเป็น: Arraylist narraylist = ArrayList ใหม่ (Arrays.asList ());


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

1

ต่อไปนี้เป็นตัวอย่างโค้ดจากอาร์เรย์

public static <T> List<T> asList(T... a) {
        return new ArrayList<>(a);
    }

    /**
     * @serial include
     */
    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
        private static final long serialVersionUID = -2764017481108945198L;
        private final E[] a;

ดังนั้นสิ่งที่เกิดขึ้นคือเมื่อเมธอด asList ถูกเรียกใช้มันจะส่งคืนรายการของคลาสสแตติกส่วนตัวของตัวเองซึ่งไม่ได้แทนที่การเพิ่ม funcion จาก AbstractList เพื่อเก็บองค์ประกอบในอาร์เรย์ ดังนั้นโดยค่าเริ่มต้นวิธีการเพิ่มในรายการนามธรรมพ่นข้อยกเว้น

ดังนั้นจึงไม่ใช่รายการอาร์เรย์ปกติ


1

คุณไม่สามารถลบและไม่สามารถเพิ่มไปยังรายการอาร์เรย์ขนาดคงที่ได้

แต่คุณสามารถสร้างรายการย่อยจากรายการนั้น

list = list.subList(0, list.size() - (list.size() - count));

public static String SelectRandomFromTemplate(String template, int count) {
   String[] split = template.split("\\|");
   List<String> list = Arrays.asList(split);
   Random r = new Random();
   while( list.size() > count ) {
      list = list.subList(0, list.size() - (list.size() - count));
   }
   return StringUtils.join(list, ", ");
}

* วิธีอื่นคือ

ArrayList<String> al = new ArrayList<String>(Arrays.asList(template));

สิ่งนี้จะสร้าง ArrayList ซึ่งไม่ใช่ขนาดคงที่เช่น Arrays.asList


0

Arrays.asList() ใช้อาร์เรย์ขนาดคงที่ภายใน
คุณไม่สามารถเพิ่มหรือลบแบบไดนามิกได้Arrays.asList()

ใช้สิ่งนี้

Arraylist<String> narraylist=new ArrayList(Arrays.asList());

ในnarraylistคุณสามารถเพิ่มหรือลบรายการ


0

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

ข้อผิดพลาดในการขว้างรหัส -

List<String> list = new ArrayList<>();
   for (String s: list) {
     if(s is null or blank) {
        list.remove(s);
     }
   }
desiredObject.setValue(list);

หลังจากแก้ไข -

 List<String> list = new ArrayList<>();
 List<String> newList= new ArrayList<>();
 for (String s: list) {
   if(s is null or blank) {
      continue;
   }
   newList.add(s);
 }
 desiredObject.setValue(newList);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.