Java ArrayList - ฉันจะทราบได้อย่างไรว่าสองรายการมีค่าเท่ากันหรือไม่?


133

ฉันมีสองArrayListประเภทAnswer(คลาสที่สร้างตัวเอง)

ฉันต้องการเปรียบเทียบทั้งสองรายการเพื่อดูว่ามีเนื้อหาเดียวกัน แต่ไม่มีคำสั่งที่สำคัญ

ตัวอย่าง:

//These should be equal.
ArrayList<String> listA = {"a", "b", "c"}
ArrayList<String> listB = {"b", "c", "a"}

List.equalsระบุว่ารายการสองรายการมีค่าเท่ากันหากมีขนาดเนื้อหาและลำดับขององค์ประกอบเดียวกัน ฉันต้องการสิ่งเดียวกัน แต่ไม่มีคำสั่งเกี่ยวกับเรื่องนี้

มีวิธีง่าย ๆ ในการทำเช่นนี้? หรือฉันจะต้องทำการวนซ้ำเพื่อวนซ้ำและตรวจสอบดัชนีแต่ละรายการของทั้งสองรายการด้วยตนเองหรือไม่

หมายเหตุ: ฉันไม่สามารถเปลี่ยนพวกเขาArrayListเป็นรายการประเภทอื่นได้


4
ดูคำตอบสำหรับคำถามนี้: stackoverflow.com/a/1075699/1133011
David Kroukamp

ดูที่ List.containAll (รายการ) ใน java
Sahil Jain

คำตอบ:


130

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

public  boolean equalLists(List<String> one, List<String> two){     
    if (one == null && two == null){
        return true;
    }

    if((one == null && two != null) 
      || one != null && two == null
      || one.size() != two.size()){
        return false;
    }

    //to avoid messing the order of the lists we will use a copy
    //as noted in comments by A. R. S.
    one = new ArrayList<String>(one); 
    two = new ArrayList<String>(two);   

    Collections.sort(one);
    Collections.sort(two);      
    return one.equals(two);
}

20
เพียงจำไว้ว่าอย่าทำลายลำดับของรายการดั้งเดิม (เช่นเดียวกับCollections.sort) - คือส่งสำเนา
arshajii

@ ARS ใช่ว่าเป็นผลข้างเคียงที่แน่นอน แต่เฉพาะในกรณีของพวกเขา
Jacob Schoen

3
คุณสามารถเพิ่มone = new ArrayList<String>(one); two = new ArrayList<String>(two);เพื่อหลีกเลี่ยงการทำลายข้อโต้แย้ง
arshajii

@jschoen การพยายามทำ Collections.sort () ทำให้ฉันมีข้อผิดพลาดนี้: Bound mismatch: การเรียงลำดับวิธีทั่วไป (รายการ <T>) ประเภทการรวบรวมไม่สามารถใช้กับอาร์กิวเมนต์ (ArrayList <Answer>) ชนิดที่อนุมานแล้วคำตอบไม่ใช่สิ่งทดแทนที่ถูกต้องสำหรับพารามิเตอร์ที่มีขอบเขต <T ขยายแบบ Comparable <? super T >>
iaacp

3
ที่สอง "if" คำสั่งภายในฟังก์ชั่นสามารถลดความซับซ้อนได้เช่นกันif(one == null || two == null || one.size() != two.size()){ return false; }เพราะคุณได้ตรวจสอบแล้วว่าทั้งหนึ่งและสองเป็นโมฆะ
Hugo

151

อาจเป็นวิธีที่ง่ายที่สุดสำหรับรายการใด ๆ :

listA.containsAll(listB) && listB.containsAll(listA)

5
ความสนุกอยู่ที่ไหน ในทุกกรณีแม้ว่านี่จะเป็นทางออกที่ดีกว่า
Jacob Schoen

67
ขึ้นอยู่กับว่า[a, b, c]และ[c, b, a, b]จะถือว่ามีเนื้อหาเดียวกัน คำตอบนี้จะบอกว่าพวกเขาทำ แต่อาจเป็นได้สำหรับ OP ที่พวกเขาไม่ได้ (เนื่องจากมีหนึ่งที่ซ้ำกันและอื่น ๆ ไม่ได้) ไม่มีอะไรจะพูดถึงปัญหาเรื่องประสิทธิภาพ
yshavit

4
การปรับปรุงตามความคิดเห็น - System.out.println (((l1.size () == l2.size ()) && l2.containAll (l1) && l1.containAll (l2));
Nrj

8
@Nrj แล้ว [1,2,2] และ [2,1,1] เป็นอย่างไร
ROMANIA_engineer

3
วิธีการนี้มีความซับซ้อนของ O (n ^ 2) พิจารณาสองรายการที่เรียงลำดับกลับกันเช่น: [1,2,3] และ [3,2,1] สำหรับองค์ประกอบแรกนั้นจะต้องสแกนองค์ประกอบ n สำหรับองค์ประกอบ n-1 ที่สองเป็นต้น ดังนั้นความซับซ้อนจะเป็นไปตามลำดับ n ^ 2 ฉันคิดว่าวิธีที่ดีกว่าคือการจัดเรียงแล้วใช้เท่ากับ มันจะมีความซับซ้อนของ O (n * log (n))
puneet

90

Apache Commonsคอลเล็กชันเพื่อช่วยเหลืออีกครั้ง:

List<String> listA = Arrays.asList("a", "b", "b", "c");
List<String> listB = Arrays.asList("b", "c", "a", "b");
System.out.println(CollectionUtils.isEqualCollection(listA, listB)); // true

 

List<String> listC = Arrays.asList("a", "b", "c");
List<String> listD = Arrays.asList("a", "b", "c", "c");
System.out.println(CollectionUtils.isEqualCollection(listC, listD)); // false

เอกสาร:

org.apache.commons.collections4.CollectionUtils

public static boolean isEqualCollection(java.util.Collection a,
                                        java.util.Collection b)

ส่งคืนtrueiff Collections ที่ให้นั้นมีองค์ประกอบที่เหมือนกันทุกประการพร้อม ๆ กัน

นั่นคือ IFF cardinality ของอีในเท่ากับ cardinality ของอีในสำหรับแต่ละองค์ประกอบอีในหรือข

พารามิเตอร์:

  • a - ชุดแรกจะต้องไม่เป็น null
  • b - ชุดที่สองต้องไม่เป็น null

คืนค่า trueถ้า iff คอลเลกชันมีองค์ประกอบเดียวกันที่มีความสำคัญเหมือนกัน


การใช้งานดูเหมือนจะคล้ายกันมากขึ้นหรือน้อยลงกับคำตอบของ DiddiZ
227353

ตกลง ... แต่สิ่งที่เกี่ยวกับการจับผู้กระทำความผิด (องค์ประกอบที่ไม่ธรรมดาในสองรายการ) ถ้าคำตอบคือfalseอะไร? ดูคำตอบของฉัน
ไมค์หนู

implementation 'org.apache.commons:commons-collections4:4.3'ได้ทิ้งฉันกับข้อผิดพลาดเส้นทางCaused by: com.android.builder.dexing.DexArchiveBuilderException: Failed to process
Aliton Oliveira

13
// helper class, so we don't have to do a whole lot of autoboxing
private static class Count {
    public int count = 0;
}

public boolean haveSameElements(final List<String> list1, final List<String> list2) {
    // (list1, list1) is always true
    if (list1 == list2) return true;

    // If either list is null, or the lengths are not equal, they can't possibly match 
    if (list1 == null || list2 == null || list1.size() != list2.size())
        return false;

    // (switch the two checks above if (null, null) should return false)

    Map<String, Count> counts = new HashMap<>();

    // Count the items in list1
    for (String item : list1) {
        if (!counts.containsKey(item)) counts.put(item, new Count());
        counts.get(item).count += 1;
    }

    // Subtract the count of items in list2
    for (String item : list2) {
        // If the map doesn't contain the item here, then this item wasn't in list1
        if (!counts.containsKey(item)) return false;
        counts.get(item).count -= 1;
    }

    // If any count is nonzero at this point, then the two lists don't match
    for (Map.Entry<String, Count> entry : counts.entrySet()) {
        if (entry.getValue().count != 0) return false;
    }

    return true;
}

ว้าวรู้สึกประหลาดใจจริงๆที่พบว่าการทำงานนี้เร็วกว่าโซลูชันอื่น ๆ ทั้งหมด และรองรับช่วงต้น
DiddiZ

สิ่งนี้อาจทำให้เกิดการลัดวงจรในลูปที่สองหากcountกลายเป็นลบทำให้ร่างกายลูปง่ายขึ้นไปif(!counts.containsKey(item) || --counts.get(item).count < 0) return false;ยังลูปที่ 3 สามารถลดความซับซ้อนลงได้for(Count c: counts.values()) if(c.count != 0) return false;
Holger

@ Holger ฉันจะพิจารณาสิ่งที่คล้ายกับครั้งแรก (ลบนับเมื่อมันฮิตศูนย์ซึ่งจะมีผลเหมือนกัน แต่ยังเปิดการตรวจสอบในตอนท้ายเป็น "กลับไม่ว่าcountsจะว่างเปล่า") แต่ไม่ต้องการปิดบัง ประเด็นหลัก: การใช้แผนที่โดยทั่วไปจะทำให้สิ่งนี้กลายเป็นปัญหา O (N + M) และเป็นแรงกระตุ้นที่ยิ่งใหญ่ที่สุดที่คุณจะได้รับ
cHao

@cHao ใช่คุณสมควรได้รับเครดิตสำหรับการชี้ไปยังโซลูชันที่มีความซับซ้อนของเวลาที่ดีกว่าครั้งก่อน ฉันเพิ่งคิดเกี่ยวกับมันเพราะมีคำถามคล้ายกันเมื่อเร็ว ๆ นี้เกี่ยวกับ iterables เนื่องจากตอนนี้เรามี Java 8 อยู่ด้วยดังนั้นจึงควรคิดใหม่ หากคุณลัดวงจรในวงที่สองเมื่อตัวเลขกลายเป็นลบวงที่สามจะล้าสมัย นอกจากนี้การหลีกเลี่ยงการชกมวยอาจเป็นดาบสองคมด้วยการMap.mergeใช้เลขจำนวนเต็มแบบกล่องอาจจะง่ายกว่าและมีประสิทธิภาพมากกว่าสำหรับกรณีการใช้งานส่วนใหญ่ ดูคำตอบนี้ด้วย ...
Holger

10

ฉันจะบอกว่าคำตอบเหล่านี้พลาด

โบลชในจาวาที่มีผลบังคับใช้ที่ยอดเยี่ยมและกระชับของเขาได้กล่าวไว้ในข้อ 47 ในหัวข้อ "รู้จักและใช้งานห้องสมุด", "เพื่อสรุปอย่ารวมวงล้อใหม่" และเขาให้เหตุผลที่ชัดเจนหลายประการว่าทำไมไม่

มีคำตอบเล็กน้อยที่นี่ซึ่งแนะนำวิธีการต่างๆจากCollectionUtilsในห้องสมุด Apache Commons Collections แต่ไม่มีใครเห็นวิธีตอบคำถามที่สวยงามและงดงามที่สุด :

Collection<Object> culprits = CollectionUtils.disjunction( list1, list2 );
if( ! culprits.isEmpty() ){
  // ... do something with the culprits, i.e. elements which are not common

}

culprits : Listsคือองค์ประกอบที่ไม่ตรงกันทั้งสอง กำหนดซึ่งต้นเหตุที่อยู่list1และที่จะlist2ค่อนข้างตรงไปตรงมาใช้และCollectionUtils.intersection( list1, culprits ) อย่างไรก็ตามมันมีแนวโน้มที่จะกระจุยในกรณีเช่น {"a", "a", "b"} ด้วย {"a", "b", "b"} ... ยกเว้นว่านี่ไม่ใช่ความล้มเหลวของซอฟต์แวร์ แต่ โดยธรรมชาติของ subtleties / ambiguities ของงานที่ต้องการCollectionUtils.intersection( list2, culprits )
disjunction

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


NB ฉันรู้สึกผิดหวังในตอนแรกว่าไม่มีCollectionUtilsวิธีใดที่จะช่วยให้คุณสามารถกำหนดเวอร์ชันของตัวเองได้มากเกินไปComparator(เพื่อให้คุณสามารถกำหนดใหม่equalsให้เหมาะกับวัตถุประสงค์ของคุณ)

แต่จากคอลเล็กชัน 4 4.0 มีคลาสใหม่Equatorซึ่ง "กำหนดความเท่าเทียมกันระหว่างวัตถุประเภท T" ในการตรวจสอบซอร์สโค้ดของ collection4 CollectionUtils.java พวกเขาดูเหมือนจะใช้สิ่งนี้กับวิธีการบางอย่าง แต่เท่าที่ฉันสามารถทำได้สิ่งนี้ไม่สามารถใช้ได้กับวิธีการที่ด้านบนของไฟล์โดยใช้CardinalityHelperคลาส ... ซึ่ง รวมและ disjunctionintersection

ฉันเดาว่าคน Apache ยังไม่ได้ทำสิ่งนี้เพราะมันไม่ใช่เรื่องไร้สาระคุณจะต้องสร้างบางสิ่งเช่นคลาส "AbstractEquatingCollection" ซึ่งแทนที่จะใช้องค์ประกอบโดยธรรมชาติequalsและhashCodeวิธีการแทนที่จะใช้สิ่งเหล่านั้น ของEquatorทุกวิธีการขั้นพื้นฐานเช่นadd, containsฯลฯ NB ในความเป็นจริงเมื่อคุณดูรหัสที่มาAbstractCollectionไม่ใช้addหรือไม่ subclasses นามธรรมเช่นAbstractSet... คุณต้องรอจนกว่าการเรียนคอนกรีตเช่นHashSetและArrayListก่อนที่จะaddถูกนำไปใช้ ปวดหัวค่อนข้าง

ในขณะที่ดูพื้นที่นี้ฉันคิดว่า ทางออกชั่วคราวที่เห็นได้ชัดคือการห่อองค์ประกอบทั้งหมดของคุณในคลาส wrapper bespoke ซึ่งใช้equalsและhashCodeใช้ชนิดของความเสมอภาคที่คุณต้องการ ... จากนั้นจัดการกับCollectionsวัตถุ wrapper เหล่านี้


นอกจากนี้บางคนฉลาดกล่าวว่า "รู้ค่าใช้จ่ายของการพึ่งพา"
Stanislaw Baranski

@StanislawBaranski นั่นเป็นความคิดเห็นที่น่าสนใจ มันเป็นข้อเสนอแนะที่ไม่ควรขึ้นอยู่กับห้องสมุดเช่นนี้หรือไม่? เมื่อคุณใช้ระบบปฏิบัติการบนคอมพิวเตอร์มันมีความเชื่ออย่างมากอยู่ใช่ไหม? เหตุผลที่ฉันมีความสุขที่จะใช้ห้องสมุด Apache เป็นเพราะฉันพาพวกเขาไปที่มีคุณภาพสูงมากและคิดว่าวิธีการของพวกเขาปฏิบัติตาม "สัญญา" ของพวกเขาและได้รับการทดสอบอย่างละเอียด คุณใช้เวลาในการพัฒนาโค้ดของคุณเองนานเท่าไหร่ซึ่งคุณน่าเชื่อถือมากขึ้น การคัดลอกรหัสจากไลบรารี Apache แบบโอเพ่นซอร์สและพิจารณาอย่างละเอียดว่าอาจเป็นเรื่องที่คุณต้องคำนึงถึง ...
mike rodent

8

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

boolean result = new HashSet<>(listA).equals(new HashSet<>(listB));

นี้จะสร้างSetออกจากกันListและจากนั้นใช้HashSet's equalsวิธีการที่ (แน่นอน) สภาพแวดล้อมการสั่งซื้อ

หาก cardinality เรื่องแล้วคุณจะต้อง จำกัด ตัวเองเพื่ออำนวยความสะดวกให้โดยList; คำตอบของ @jschoen น่าจะเหมาะกว่าในกรณีนั้น


เกิดอะไรขึ้นถ้า listA = [a, b, c, c] และ listB = [a, b, c] ผลลัพธ์จะเป็นจริง แต่รายการไม่เท่ากับ
Nikolas

6

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

static <T> boolean equalsIgnoreOrder(List<T> a, List<T> b) {
    return ImmutableMultiset.copyOf(a).equals(ImmutableMultiset.copyOf(b));
}

assert equalsIgnoreOrder(ImmutableList.of(3, 1, 2), ImmutableList.of(2, 1, 3));
assert !equalsIgnoreOrder(ImmutableList.of(1), ImmutableList.of(1, 1));

6

สิ่งนี้ขึ้นอยู่กับโซลูชัน @cHao ฉันรวมการแก้ไขและการปรับปรุงประสิทธิภาพไว้หลายประการ สิ่งนี้จะทำงานโดยเร็วเป็นสองเท่าของวิธีแก้ปัญหาที่ได้รับคำสั่งเท่ากับสำเนา ใช้งานได้กับคอลเลกชันทุกประเภท คอลเลกชันที่ว่างเปล่าและ null ถือได้ว่าเท่ากัน ใช้เพื่อประโยชน์ของคุณ;)

/**
 * Returns if both {@link Collection Collections} contains the same elements, in the same quantities, regardless of order and collection type.
 * <p>
 * Empty collections and {@code null} are regarded as equal.
 */
public static <T> boolean haveSameElements(Collection<T> col1, Collection<T> col2) {
    if (col1 == col2)
        return true;

    // If either list is null, return whether the other is empty
    if (col1 == null)
        return col2.isEmpty();
    if (col2 == null)
        return col1.isEmpty();

    // If lengths are not equal, they can't possibly match
    if (col1.size() != col2.size())
        return false;

    // Helper class, so we don't have to do a whole lot of autoboxing
    class Count
    {
        // Initialize as 1, as we would increment it anyway
        public int count = 1;
    }

    final Map<T, Count> counts = new HashMap<>();

    // Count the items in col1
    for (final T item : col1) {
        final Count count = counts.get(item);
        if (count != null)
            count.count++;
        else
            // If the map doesn't contain the item, put a new count
            counts.put(item, new Count());
    }

    // Subtract the count of items in col2
    for (final T item : col2) {
        final Count count = counts.get(item);
        // If the map doesn't contain the item, or the count is already reduced to 0, the lists are unequal 
        if (count == null || count.count == 0)
            return false;
        count.count--;
    }

    // At this point, both collections are equal.
    // Both have the same length, and for any counter to be unequal to zero, there would have to be an element in col2 which is not in col1, but this is checked in the second loop, as @holger pointed out.
    return true;
}

คุณสามารถข้ามขั้นตอนสุดท้ายสำหรับลูปโดยใช้ตัวนับผลรวม ตัวนับผลรวมจะนับผลรวมของการนับในแต่ละด่าน เพิ่มตัวนับผลรวมใน for-loop แรกและลดลงใน for-loop ที่สอง หากตัวนับผลรวมมากกว่า 0 รายการจะไม่ตรงกันมิฉะนั้นจะเกิดขึ้น ในปัจจุบันใน for-loop สุดท้ายคุณตรวจสอบว่าการนับทั้งหมดเป็นศูนย์หรือในคำอื่น ๆ หากผลรวมของการนับทั้งหมดเป็นศูนย์ การใช้ชนิดตัวนับผลรวมของการย้อนกลับของการตรวจสอบนี้กลับเป็นจริงถ้าผลรวมของการนับเป็นศูนย์หรือเท็จอย่างอื่น
SatA

IMO เป็นสิ่งที่ควรค่าแก่การข้ามไปเนื่องจาก for-loop มีรายการที่ตรงกัน
SatA

@SATA จริง ๆ แล้วคุณสามารถลบลูปที่ 3 โดยไม่ต้องเปลี่ยนใด ๆ ลูปที่สองกลับมาแล้วfalseเมื่อไม่มีคีย์หรือจำนวนนั้นกลายเป็นลบ เนื่องจากขนาดทั้งหมดของทั้งสองรายการตรงกัน (สิ่งนี้ได้รับการตรวจสอบล่วงหน้า) จึงเป็นไปไม่ได้ที่จะมีค่าที่ไม่ใช่ศูนย์หลังจากวนรอบที่สองเนื่องจากไม่มีค่าบวกสำหรับคีย์โดยไม่มีค่าลบสำหรับคีย์อื่น
Holger

@holger ดูเหมือนว่าคุณจะถูกต้องอย่างแน่นอน เท่าที่ฉันสามารถบอกได้ลูปที่ 3 ไม่จำเป็นเลย
SatA

@SATA …และด้วย Java 8 สิ่งนี้สามารถนำไปใช้ได้อย่างรัดกุมเช่นเดียวกับคำตอบนี้
Holger

5

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

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

อีกวิธีหนึ่งคือการผ่านแต่ละรายการนับจำนวนครั้งที่แต่ละองค์ประกอบปรากฏขึ้น หากทั้งสองรายการมีจำนวนที่เหมือนกันในตอนท้ายพวกเขาจะมีองค์ประกอบที่เหมือนกัน รหัสสำหรับที่จะแปลแต่ละรายการเป็นแผนที่elem -> (# of times the elem appears in the list)แล้วโทรequalsบนแผนที่ทั้งสอง หากแผนที่เป็นHashMapเช่นนั้นการแปลแต่ละคำนั้นเป็นการดำเนินการ O (N) เช่นเดียวกับการเปรียบเทียบ นั่นจะให้อัลกอริทึมที่มีประสิทธิภาพในแง่ของเวลาโดยมีหน่วยความจำเพิ่มเติม


5

ฉันมีปัญหาเดียวกันนี้และหาวิธีแก้ไขปัญหาที่แตกต่างออกไป อันนี้ยังใช้งานได้เมื่อมีส่วนร่วมที่ซ้ำกัน:

public static boolean equalsWithoutOrder(List<?> fst, List<?> snd){
  if(fst != null && snd != null){
    if(fst.size() == snd.size()){
      // create copied lists so the original list is not modified
      List<?> cfst = new ArrayList<Object>(fst);
      List<?> csnd = new ArrayList<Object>(snd);

      Iterator<?> ifst = cfst.iterator();
      boolean foundEqualObject;
      while( ifst.hasNext() ){
        Iterator<?> isnd = csnd.iterator();
        foundEqualObject = false;
        while( isnd.hasNext() ){
          if( ifst.next().equals(isnd.next()) ){
            ifst.remove();
            isnd.remove();
            foundEqualObject = true;
            break;
          }
        }

        if( !foundEqualObject ){
          // fail early
          break;
        }
      }
      if(cfst.isEmpty()){ //both temporary lists have the same size
        return true;
      }
    }
  }else if( fst == null && snd == null ){
    return true;
  }
  return false;
}

ข้อดีเมื่อเปรียบเทียบกับโซลูชันอื่น ๆ :

  • ความซับซ้อนน้อยกว่า O (N²) (แม้ว่าฉันยังไม่ได้ทดสอบมันเป็นประสิทธิภาพที่แท้จริงเมื่อเปรียบเทียบกับโซลูชันในคำตอบอื่น ๆ ที่นี่);
  • ออกเร็ว
  • ตรวจสอบ null;
  • ทำงานได้แม้ในกรณีที่มีรายการที่ซ้ำกัน: ถ้าคุณมีอาเรย์[1,2,3,3]และอีกอาเรย์[1,2,2,3]ส่วนใหญ่ที่นี่บอกคุณว่าพวกเขาจะเหมือนกันเมื่อไม่ได้พิจารณาคำสั่ง โซลูชันนี้จะหลีกเลี่ยงสิ่งนี้โดยลบองค์ประกอบที่เท่ากันออกจากรายการชั่วคราว
  • ใช้ semantic equality ( equals) และไม่อ้างอิง equality ( ==);
  • ไม่เรียงลำดับ itens ดังนั้นจึงไม่จำเป็นต้องเรียงลำดับ (ตามimplement Comparable) เพื่อให้โซลูชันนี้ทำงานได้

3

หากคุณไม่ต้องการเรียงลำดับคอลเลกชันและคุณต้องการผลลัพธ์ที่ ["A" "B" "C"] นั้นไม่เท่ากับ ["B" "B" "A" "C"],

l1.containsAll(l2)&&l2.containsAll(l1)

ไม่เพียงพอคุณต้องตรวจสอบขนาดด้วย:

    List<String> l1 =Arrays.asList("A","A","B","C");
    List<String> l2 =Arrays.asList("A","B","C");
    List<String> l3 =Arrays.asList("A","B","C");

    System.out.println(l1.containsAll(l2)&&l2.containsAll(l1));//cautions, this will be true
    System.out.println(isListEqualsWithoutOrder(l1,l2));//false as expected

    System.out.println(l3.containsAll(l2)&&l2.containsAll(l3));//true as expected
    System.out.println(isListEqualsWithoutOrder(l2,l3));//true as expected


    public static boolean isListEqualsWithoutOrder(List<String> l1, List<String> l2) {
        return l1.size()==l2.size() && l1.containsAll(l2)&&l2.containsAll(l1);
}

2

โซลูชันที่ใช้วิธีการลบ CollectionUtils:

import static org.apache.commons.collections15.CollectionUtils.subtract;

public class CollectionUtils {
  static public <T> boolean equals(Collection<? extends T> a, Collection<? extends T> b) {
    if (a == null && b == null)
      return true;
    if (a == null || b == null || a.size() != b.size())
      return false;
    return subtract(a, b).size() == 0 && subtract(a, b).size() == 0;
  }
}

1

หากคุณสนใจคำสั่งซื้อให้ใช้วิธีเท่ากับ:

list1.equals(list2)

หากคุณไม่สนใจคำสั่งซื้อให้ใช้สิ่งนี้

Collections.sort(list1);
Collections.sort(list2);      
list1.equals(list2)

4
เขาบอกว่าเขาไม่สนใจเรื่องระเบียบ
mike rodent

0

ดีที่สุดของทั้งสองโลก [@DiddiZ, @Chalkos]: อันนี้ส่วนใหญ่สร้างตามวิธีการ @Chalkos แต่แก้ไขข้อผิดพลาด (ifst.next ()) และปรับปรุงการตรวจสอบเริ่มต้น (นำมาจาก @DiddiZ) เช่นเดียวกับขจัดความต้องการ คัดลอกคอลเล็กชันแรก (เพียงลบไอเท็มออกจากสำเนาของคอลเล็กชันที่สอง)

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

public static <T> boolean isCollectionMatch(Collection<T> one, Collection<T> two) {
    if (one == two)
        return true;

    // If either list is null, return whether the other is empty
    if (one == null)
        return two.isEmpty();
    if (two == null)
        return one.isEmpty();

    // If lengths are not equal, they can't possibly match
    if (one.size() != two.size())
        return false;

    // copy the second list, so it can be modified
    final List<T> ctwo = new ArrayList<>(two);

    for (T itm : one) {
        Iterator<T> it = ctwo.iterator();
        boolean gotEq = false;
        while (it.hasNext()) {
            if (itm.equals(it.next())) {
                it.remove();
                gotEq = true;
                break;
            }
        }
        if (!gotEq) return false;
    }
    // All elements in one were found in two, and they're the same size.
    return true;
}

ถ้าฉันไม่เข้าใจผิดความซับซ้อนของอัลกอริทึมนี้ในสถานการณ์ที่คุ้มค่า (ซึ่งรายการเท่ากัน แต่เรียงในลักษณะตรงกันข้าม) จะเป็น O (N * N!)
SatA

ที่จริงแล้วมันจะเป็น O (N * (N / 2)) เช่นเดียวกับการทำซ้ำแต่ละครั้งขนาดอาร์เรย์จะลดลง
jazzgil

0

มันเป็นวิธีทางเลือกในการตรวจสอบความเท่าเทียมกันของรายการอาเรย์ซึ่งสามารถมีค่า Null:

List listA = Arrays.asList(null, "b", "c");
List listB = Arrays.asList("b", "c", null);

System.out.println(checkEquality(listA, listB)); // will return TRUE


private List<String> getSortedArrayList(List<String> arrayList)
{
    String[] array = arrayList.toArray(new String[arrayList.size()]);

    Arrays.sort(array, new Comparator<String>()
    {
        @Override
        public int compare(String o1, String o2)
        {
            if (o1 == null && o2 == null)
            {
                return 0;
            }
            if (o1 == null)
            {
                return 1;
            }
            if (o2 == null)
            {
                return -1;
            }
            return o1.compareTo(o2);
        }
    });

    return new ArrayList(Arrays.asList(array));
}

private Boolean checkEquality(List<String> listA, List<String> listB)
{
    listA = getSortedArrayList(listA);
    listB = getSortedArrayList(listB);

    String[] arrayA = listA.toArray(new String[listA.size()]);
    String[] arrayB = listB.toArray(new String[listB.size()]);

    return Arrays.deepEquals(arrayA, arrayB);
}

การคัดลอกข้อมูลระหว่างรายการและอาร์เรย์คืออะไร
Holger

0

โซลูชันของฉันสำหรับสิ่งนี้ มันไม่เจ๋งนัก แต่ทำงานได้ดี

public static boolean isEqualCollection(List<?> a, List<?> b) {

    if (a == null || b == null) {
        throw new NullPointerException("The list a and b must be not null.");
    }

    if (a.size() != b.size()) {
        return false;
    }

    List<?> bCopy = new ArrayList<Object>(b);

    for (int i = 0; i < a.size(); i++) {

        for (int j = 0; j < bCopy.size(); j++) {
            if (a.get(i).equals(bCopy.get(j))) {
                bCopy.remove(j);
                break;
            }
        }
    }

    return bCopy.isEmpty();
}

-1

ในกรณีนั้นรายการ {"a", "b"} และ {"b", "a"} เท่ากัน และ {"a", "b"} และ {"b", "a", "c"} ไม่เท่ากัน หากคุณใช้รายการของวัตถุที่ซับซ้อนอย่าลืมแทนที่เมธอดเท่ากับเพราะcontainAllจะใช้มันภายใน

if (oneList.size() == secondList.size() && oneList.containsAll(secondList)){
        areEqual = true;
}

-1: ช่วยให้คำตอบที่ผิดปกติกับ { "A", "A", "B"} และ { "A", "B", "B"}: AbstractCollection.containsAll()ตรวจสอบซอร์สโค้ด คุณจะต้องอนุญาตให้มีองค์ประกอบที่ซ้ำกันในขณะที่เรากำลังพูดถึงไม่ได้เกี่ยวกับLists Setsโปรดดูคำตอบของฉัน
mike rodent
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.