มีวิธีที่ดีกว่าในการรวมชุดสตริงสองชุดใน java หรือไม่?


91

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

Set<String> oldStringSet = getOldStringSet();
Set<String> newStringSet = getNewStringSet();

for(String currentString : oldStringSet)
{
    if (!newStringSet.contains(currentString))
    {
        newStringSet.add(currentString);
    }
}

คำตอบ:


117

เนื่องจาก a Setไม่มีรายการที่ซ้ำกันคุณจึงสามารถรวมทั้งสองโดย:

newStringSet.addAll(oldStringSet);

ไม่สำคัญว่าคุณจะเพิ่มสิ่งต่างๆสองครั้งชุดจะมีองค์ประกอบเพียงครั้งเดียว ... เช่นไม่จำเป็นต้องตรวจสอบโดยใช้containsวิธี


92

คุณสามารถทำได้โดยใช้ซับเดียวนี้

Set<String> combined = Stream.concat(newStringSet.stream(), oldStringSet.stream())
        .collect(Collectors.toSet());

ด้วยการนำเข้าแบบคงที่จะดูดีกว่า

Set<String> combined = concat(newStringSet.stream(), oldStringSet.stream())
        .collect(toSet());

อีกวิธีหนึ่งคือการใช้วิธีflatMap :

Set<String> combined = Stream.of(newStringSet, oldStringSet).flatMap(Set::stream)
        .collect(toSet());

นอกจากนี้คอลเลกชันใด ๆ ก็สามารถรวมเข้ากับองค์ประกอบเดียวได้อย่างง่ายดาย

Set<String> combined = concat(newStringSet.stream(), Stream.of(singleValue))
        .collect(toSet());

ดีกว่า addAll อย่างไร
KKlalala

7
@Klalala ความต้องการของคุณจะเป็นตัวกำหนดว่าอันไหนดีกว่า ความแตกต่างที่สำคัญระหว่างaddAllและการใช้สตรีม: •ใช้จะมีผลข้างเคียงของการเปลี่ยนแปลงทางร่างกายเนื้อหาของset1.addAll(set2) set1•อย่างไรก็ตามการใช้สตรีมจะทำให้อินสแตนซ์ใหม่Setมีเนื้อหาของทั้งสองชุดเสมอโดยไม่ต้องแก้ไขอินสแตนซ์ Set ดั้งเดิม IMHO คำตอบนี้ดีกว่าเพราะหลีกเลี่ยงผลข้างเคียงและอาจเกิดการเปลี่ยนแปลงที่ไม่คาดคิดกับชุดเดิมหากต้องใช้ที่อื่นในขณะที่คาดหวังว่าจะได้เนื้อหาต้นฉบับ HTH
edwardsmatt

1
นอกจากนี้ยังมีข้อดีในการรองรับชุดไม่เปลี่ยนรูป ดู: docs.oracle.com/javase/8/docs/api/java/util/…
edwardsmatt


12

จากนิยามชุดประกอบด้วยองค์ประกอบที่ไม่ซ้ำกันเท่านั้น

Set<String> distinct = new HashSet<String>(); 
 distinct.addAll(oldStringSet);
 distinct.addAll(newStringSet);

เพื่อปรับปรุงโค้ดของคุณคุณอาจสร้างวิธีการทั่วไปสำหรับสิ่งนั้น

public static <T> Set<T> distinct(Collection<T>... lists) {
    Set<T> distinct = new HashSet<T>();

    for(Collection<T> list : lists) {
        distinct.addAll(list);
    }
    return distinct;
}

7

หากคุณใช้ Guava คุณสามารถใช้เครื่องมือสร้างเพื่อให้มีความยืดหยุ่นมากขึ้น:

ImmutableSet.<String>builder().addAll(someSet)
                              .addAll(anotherSet)
                              .add("A single string")
                              .build();

4

เพียงแค่ใช้newStringSet.addAll(oldStringSet). ไม่จำเป็นต้องตรวจสอบรายการที่ซ้ำกันเนื่องจากการSetใช้งานได้ดำเนินการไปแล้ว


3

http://docs.oracle.com/javase/7/docs/api/java/util/Set.html#addAll(java.util.Collection )

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



2

ใช้boolean addAll(Collection<? extends E> c)
เพิ่มองค์ประกอบทั้งหมดในคอลเล็กชันที่ระบุลงในชุดนี้หากยังไม่มีอยู่ (การดำเนินการเพิ่มเติม) ถ้าคอลเลกชันที่ระบุเป็นชุดด้วยการดำเนินการ addAll จะแก้ไขชุดนี้อย่างมีประสิทธิภาพเพื่อให้ค่าของมันเป็นผลรวมของสองชุด ลักษณะการทำงานของการดำเนินการนี้ไม่ได้กำหนดไว้หากคอลเลกชันที่ระบุถูกแก้ไขในขณะที่การดำเนินการอยู่ระหว่างดำเนินการ

newStringSet.addAll(oldStringSet)

2

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

Set<String> newStringSet = getNewStringSet();
Set<String> oldStringSet = getOldStringSet();

Set<String> myResult;
if(oldStringSet.size() > newStringSet.size()){
    oldStringSet.addAll(newStringSet);
    myResult = oldStringSet;
} else{
    newStringSet.addAll(oldStringSet);
    myResult = newStringSet;
}

ด้วยวิธีนี้หากชุดใหม่ของคุณมี 10 องค์ประกอบและชุดเก่าของคุณมี 100000 คุณจะดำเนินการเพียง 10 ครั้งแทนที่จะเป็น 100000


นี่เป็นตรรกะที่ดีมากที่ฉันไม่สามารถจินตนาการได้ว่าทำไมสิ่งนี้ถึงไม่อยู่ในพารามิเตอร์หลักของ addAll เช่นpublic boolean addAll(int index, Collection<? extends E> c, boolean checkSizes)
Gaspar

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

ใช่ฉันกำลังพูดวิธีการอื่นมากเกินไป
Gaspar

2

หากคุณใช้ Apache Common ให้ใช้SetUtilsคลาสจากorg.apache.commons.collections4.SetUtils;

SetUtils.union(setA, setB);

หมายเหตุสิ่งนี้จะส่งคืน a SetViewซึ่งไม่เปลี่ยนรูป
jaco0646

นอกจากนี้การได้รับขนาด () จาก a SetViewจะเป็นการดำเนินการเชิงเส้นเสมอ
Jugbot

2
Set.addAll()

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

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