วิธีการลบองค์ประกอบ null ทั้งหมดออกจาก ArrayList หรือ String Array


188

ฉันลองวนซ้ำแบบนั้น

// ArrayList tourists

for (Tourist t : tourists) {
    if (t != null) {     
        t.setId(idForm); 
    }   
}

แต่มันก็ไม่ดี ใครช่วยแนะนำฉันได้ดีกว่า


เกณฑ์มาตรฐานที่มีประโยชน์เพื่อการตัดสินใจที่ดีกว่า:

ในขณะที่ลูปสำหรับการทดสอบลูปและ Iterator


2
ใช้งานIteratorหรือไม่ ขุด java-doc download.oracle.com/javase/6/docs/api/java/util/…
Nishant

คำตอบ:


365

ลอง:

tourists.removeAll(Collections.singleton(null));

อ่านJava API รหัสจะโยนjava.lang.UnsupportedOperationExceptionสำหรับรายการที่ไม่เปลี่ยนรูป (เช่นสร้างด้วยArrays.asList); ดูคำตอบนี้สำหรับรายละเอียดเพิ่มเติม


9
ความซับซ้อนเวลาของการList.removeAll()เป็นn ^ 2 แค่พูด.
Hemanth

8
สำหรับ Java 8 หรือใหม่กว่าโปรดดู @ คำตอบของ MarcG ด้านล่าง
Andy Thomas

2
@Hemanth คุณสามารถอธิบายเกี่ยวกับวิธีการที่ซับซ้อนในเวลานั้นได้หรือไม่? เพราะมันมีลักษณะค่อนข้างO(n)ให้ฉันเพื่อทั้งสองและArrayList LinkedList
Helder Pereira

1
@HelderPereira ผมไม่คิดว่ามันควรจะเป็นสำหรับกรณีนี้เนื่องจากแหล่งที่มา (สาย 349) ดูเหมือนว่าจะห่วงผ่านทั้งสองรายการ ( contains()ลูปอาร์เรย์ทั้งหมด) และตั้งแต่เป็นเพียงองค์ประกอบหนึ่งมันจะเป็นsingleton แต่โดยทั่วไปก็จะเป็นN * 1 = N N^2
Moira

6
@Hemanth ไม่มันไม่ได้เป็น มันคือ n * m โดยที่ m คือจำนวนองค์ประกอบในกรณีนี้เป็นโมฆะเดี่ยวซึ่งก็คือ 1 มันคือ O (n) คุณสามารถดูซอร์สโค้ดได้ที่นี่และดูว่าจะอ่านและเขียนในรายการครั้งเดียวแล้วย้ายองค์ประกอบไปยังบัญชีที่โหลดใหม่
Tatarize

117

ตั้งแต่ปี 2015 นี่เป็นวิธีที่ดีที่สุด (Java 8):

tourists.removeIf(Objects::isNull);

หมายเหตุ:รหัสนี้จะjava.lang.UnsupportedOperationExceptionแสดงเป็นรายการขนาดคงที่ (เช่นสร้างด้วย Arrays.asList) รวมถึงรายการที่ไม่เปลี่ยนรูป


1
"ดีที่สุด" ในทางใด? มันเร็วกว่าวิธีอื่นหรือไม่? หรือเป็นเพียงอ่านได้ง่ายขึ้นโดยอาศัยอำนาจตามความกะทัดรัด?
Andy Thomas

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

1
Arrays.asListไม่ได้เปลี่ยนรูป ขนาดคงที่
turbanoff

@turboff ใช่คุณพูดถูก ขนาดคงที่เท่านั้นฉันจะอัปเดตคำตอบ
MarcG

46
list.removeAll(Collections.singleton(null));

มันจะโยนUnsupportedExceptionถ้าคุณใช้มันใน Arrays.asList เพราะมันให้สำเนาที่ไม่เปลี่ยนรูปดังนั้นคุณจึงไม่สามารถแก้ไขได้ ดูรหัสด้านล่าง มันสร้างสำเนาที่ไม่แน่นอนและจะไม่ทิ้งข้อยกเว้นใด ๆ

public static String[] clean(final String[] v) {
    List<String> list = new ArrayList<String>(Arrays.asList(v));
    list.removeAll(Collections.singleton(null));
    return list.toArray(new String[list.size()]);
}

18

ไม่มีประสิทธิภาพ แต่สั้น

while(tourists.remove(null));

1
น่าเสียดายที่โซลูชันของคุณเป็นสิ่งเดียวที่ใช้ได้ผลกับฉัน ... ขอบคุณ!
Pkmmte

ง่ายและรวดเร็ว

5
@imrahe ตรงข้ามกับความเป็นจริง ช้ามากถ้าคุณมีรายการใหญ่
Gewure

18

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

ImmutableList.copyOf(Iterables.filter(tourists, Predicates.notNull()))

7
 for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
      if (itr.next() == null) { itr.remove(); }
 }

สิ่งนี้มีประโยชน์มากขึ้นเมื่อคุณต้องลบองค์ประกอบในขณะที่ทำการสำรวจ บังเอิญผมได้ nulling removeAll(..null..)องค์ประกอบกว่าพยายามที่จะใช้ ขอบคุณ!
มุสตาฟา

คุณอาจจะดีกว่าการตั้งค่าเป็นโมฆะแล้วลบออกในตอนท้าย batchRemove ใน removeAll จะข้ามรายการด้วยตำแหน่งการอ่านและการเขียนและทำซ้ำรายการหนึ่งครั้งย้ายการอ่าน แต่ไม่ใช่การเขียนเมื่อมันเป็นโมฆะ ลบออก () อาจต้องมีอาเรย์ในการคัดลอกอาเรย์ทั้งหมดในแต่ละครั้งที่ถูกเรียก
Tatarize

4

Pre-Java 8 คุณควรใช้:

tourists.removeAll(Collections.singleton(null));

การใช้ Post-Java 8:

tourists.removeIf(Objects::isNull);

เหตุผลที่นี่คือความซับซ้อนของเวลา ปัญหาของอาร์เรย์คือการดำเนินการลบสามารถใช้เวลา O (n) ในการดำเนินการให้เสร็จสมบูรณ์ จริง ๆ ใน Java นี่คือสำเนาขององค์ประกอบที่เหลือจะถูกย้ายเพื่อแทนที่จุดที่ว่างเปล่า วิธีแก้ปัญหาอื่น ๆ ที่นำเสนอที่นี่จะทำให้เกิดปัญหานี้ อดีตคือเทคนิค O (n * m) โดยที่ m คือ 1 เพราะมันเป็นโมฆะเดี่ยว: ดังนั้น O (n)

คุณควรลบ singleton ทั้งหมดภายในมันจะทำการ batchRemove () ซึ่งมีตำแหน่งการอ่านและตำแหน่งการเขียน และวนซ้ำรายการ เมื่อมันชนกับโมฆะมันจะทำซ้ำตำแหน่งการอ่านโดย 1 เมื่อพวกมันเหมือนกันมันจะผ่านเมื่อมันต่างกันมันจะเคลื่อนที่ไปตามการคัดลอกค่า จากนั้นในตอนท้ายมันมีขนาด

มันทำสิ่งนี้ได้อย่างมีประสิทธิภาพภายใน:

public static <E> void removeNulls(ArrayList<E> list) {
    int size = list.size();
    int read = 0;
    int write = 0;
    for (; read < size; read++) {
        E element = list.get(read);
        if (element == null) continue;
        if (read != write) list.set(write, element);
        write++;
    }
    if (write != size) {
        list.subList(write, size).clear();
    }
}

ซึ่งคุณสามารถเห็นได้อย่างชัดเจนคือการดำเนินการ O (n)

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


for (Iterator<Tourist> itr = tourists.iterator(); itr.hasNext();) {
      if (itr.next() == null) { itr.remove(); }
 }

ในขณะที่สิ่งนี้ดูเหมือนว่ามีเหตุผลเพียงพอการลบ () บนตัววนซ้ำภายในจะเรียก:

ArrayList.this.remove(lastRet);

ซึ่งเป็นการดำเนินการ O (n) อีกครั้งภายในการลบ มันทำ System.arraycopy () ซึ่งไม่ใช่สิ่งที่คุณต้องการอีกครั้งถ้าคุณสนใจความเร็ว นี่ทำให้มันเป็น n ^ 2

นอกจากนี้ยังมี:

while(tourists.remove(null));

อันไหนคือ O (m * n ^ 2) ที่นี่เราไม่เพียง แต่ย้ำรายการ เราย้ำรายการทั้งหมดทุกครั้งที่เราจับคู่ค่าว่าง จากนั้นเราทำการดำเนินการ n / 2 (โดยเฉลี่ย) เพื่อทำ System.arraycopy () เพื่อทำการลบ คุณสามารถเรียงลำดับคอลเลกชันทั้งหมดระหว่างรายการที่มีค่าและรายการที่มีค่า Null และตัดส่วนที่ลงท้ายด้วยเวลาที่น้อยลง ในความเป็นจริงมันเป็นเรื่องจริงสำหรับคนที่เสียทั้งหมด อย่างน้อยก็ในทางทฤษฎีแล้วระบบที่แท้จริงการจัดการคัดลอกไม่ใช่การปฏิบัติงานจริงในทางปฏิบัติ ในทางทฤษฎีทฤษฎีและการปฏิบัติเป็นสิ่งเดียวกัน ในทางปฏิบัติพวกเขาไม่ได้


3

มีวิธีที่ง่ายในการลบnullค่าทั้งหมดออกจากcollectionคุณต้องผ่านคอลเลกชันที่มีค่า null เป็นพารามิเตอร์ไปยังremoveAll()วิธีการ

List s1=new ArrayList();
s1.add(null);

yourCollection.removeAll(s1);

สิ่งนี้ทำงานได้ดีที่สุดสำหรับฉัน นอกจากนี้ยังช่วยให้คุณสามารถเพิ่มมากกว่าหนึ่งรายการใน "อาร์เรย์ตัวกรอง" ของคุณที่ส่งผ่านไปยังเมธอด removeAll ของคอลเลกชันดั้งเดิมได้อย่างง่ายดาย

3

Objectsชั้นมีnonNull Predicateที่สามารถนำมาใช้กับfilterที่สามารถนำมาใช้กับ

ตัวอย่างเช่น:

tourists.stream().filter(Objects::nonNull).collect(Collectors.toList());

1
ยินดีต้อนรับสู่ Stack Overflow เมื่อตอบคำถามโปรดลองเพิ่มคำอธิบายรหัสของคุณ โปรดย้อนกลับและแก้ไขคำตอบของคุณเพื่อรวมข้อมูลเพิ่มเติม
Tyler

3

การใช้ Java 8 คุณสามารถทำได้โดยใช้stream()และfilter()

tourists = tourists.stream().filter(t -> t != null).collect(Collectors.toList())

หรือ

tourists = tourists.stream().filter(Objects::nonNull).collect(Collectors.toList())

สำหรับข้อมูลเพิ่มเติม: Java 8 - Streams


1
วิธีการแก้ปัญหานี้จะทำงานกับสำเนาที่ไม่เปลี่ยนรูปเช่น -> List <String> listOfString = Arrays.asList ("test1", null, "test"); ..... เกินไป! ขอบคุณ
Anurag_BEHS

2

นี่เป็นวิธีที่ง่ายในการลบค่า Null เริ่มต้นจาก arraylist

     tourists.removeAll(Arrays.asList(null));  

มิฉะนั้นค่าสตริง "null" จะถูกลบออกจากรายการอาร์เรย์

       tourists.removeAll(Arrays.asList("null"));  

1

ฉันเล่นกับสิ่งนี้และพบว่า trimToSize () ดูเหมือนว่าจะใช้งานได้ ฉันกำลังทำงานบนแพลตฟอร์ม Android ดังนั้นจึงอาจแตกต่างกัน


2
ตาม Javadoc ที่ไม่ได้ปรับเปลี่ยนเนื้อหาของที่trimToSize ArrayListหากนี่เป็น Android ที่แตกต่างกันมันอาจเป็นข้อผิดพลาด
fabian

1

เราสามารถใช้ตัววนซ้ำเดียวกันเพื่อลบค่า Null ทั้งหมด

Iterator<Tourist> itr= tourists.iterator();
while(itr.hasNext()){
    if(itr.next() == null){
        itr.remove();
    }
}

1

ฉันใช้ส่วนต่อประสานกระแสกับการรวบรวมกระแสข้อมูลและวิธีช่วยในการสร้างรายการใหม่

tourists.stream().filter(this::isNotNull).collect(Collectors.toList());

private <T> boolean isNotNull(final T item) {
    return  item != null;
}

2
tourists.stream().filter(s -> s != null).collect(Collectors.toList());
1ac0

1

ส่วนใหญ่ฉันใช้สิ่งนี้:

list.removeAll(Collections.singleton(null));

แต่หลังจากที่ฉันเรียนรู้ Java 8 ฉันเปลี่ยนมาใช้สิ่งนี้:

List.removeIf(Objects::isNull);

0

การใช้Java 8สามารถดำเนินการได้หลายวิธีโดยใช้สตรีมสตรีมขนานและremoveIfเมธอด:

List<String> stringList = new ArrayList<>(Arrays.asList(null, "A", "B", null, "C", null));
List<String> listWithoutNulls1 = stringList.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList()); //[A,B,C]
List<String> listWithoutNulls2 = stringList.parallelStream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList()); //[A,B,C]
stringList.removeIf(Objects::isNull); //[A,B,C]

สตรีมแบบขนานจะใช้ประโยชน์จากโปรเซสเซอร์ที่มีอยู่และจะเร่งกระบวนการสำหรับรายการขนาดที่เหมาะสม ขอแนะนำให้ใช้เกณฑ์มาตรฐานก่อนใช้สตรีมเสมอ


0

คล้ายกับ @Lithium answer แต่ไม่โยนข้อผิดพลาด "List อาจไม่มีประเภท null":

   list.removeAll(Collections.<T>singleton(null));

0
List<String> colors = new ArrayList<>(
Arrays.asList("RED", null, "BLUE", null, "GREEN"));
// using removeIf() + Objects.isNull()
colors.removeIf(Objects::isNull);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.