Java: ตรวจหารายการที่ซ้ำกันใน ArrayList?


104

ฉันจะตรวจจับ (คืนค่าจริง / เท็จ) ได้อย่างไรว่า ArrayList มีองค์ประกอบเดียวกันมากกว่าหนึ่งใน Java หรือไม่

ขอบคุณมากเทอร์รี่

แก้ไข ลืมบอกไปว่าฉันไม่ได้ต้องการเปรียบเทียบ "บล็อก" ซึ่งกันและกัน แต่เป็นค่าจำนวนเต็ม แต่ละ "บล็อก" มี int และนี่คือสิ่งที่ทำให้พวกเขาแตกต่างกัน ฉันพบ int ของบล็อกเฉพาะโดยเรียกเมธอดชื่อ "getNum" (เช่น table1 [0] [2] .getNum ();


หากเปรียบเทียบ "Block" ด้วย int คุณน่าจะมี hashCode ส่งคืน int นั้นเหมือนกันและมีค่าเท่ากับการเปรียบเทียบ int เหล่านั้น
Paul Tomblin

ใช้ Set แทน List
dmarquina

คำตอบ:


194

ง่ายที่สุด: ถ่ายโอนคอลเล็กชันทั้งหมดลงใน Set (โดยใช้ตัวสร้าง Set (Collection) หรือ Set.addAll) จากนั้นดูว่า Set มีขนาดเท่ากับ ArrayList หรือไม่

List<Integer> list = ...;
Set<Integer> set = new HashSet<Integer>(list);

if(set.size() < list.size()){
    /* There are duplicates */
}

อัปเดต: หากฉันเข้าใจคำถามของคุณอย่างถูกต้องคุณมีอาร์เรย์ 2d ของบล็อกเช่นเดียวกับใน

ตารางบล็อก [] [];

และคุณต้องการตรวจสอบว่ามีแถวที่ซ้ำกันหรือไม่?

ในกรณีนั้นฉันสามารถทำสิ่งต่อไปนี้ได้โดยสมมติว่าบล็อกใช้ "เท่ากับ" และ "hashCode" อย่างถูกต้อง:

for (Block[] row : table) {
   Set set = new HashSet<Block>(); 
   for (Block cell : row) {
      set.add(cell);
   }
   if (set.size() < 6) { //has duplicate
   }
}

ฉันไม่แน่ใจ 100% สำหรับไวยากรณ์ดังนั้นการเขียนเป็นไฟล์

for (int i = 0; i < 6; i++) {
   Set set = new HashSet<Block>(); 
   for (int j = 0; j < 6; j++)
    set.add(table[i][j]);
 ...

Set.addส่งคืนบูลีนเท็จหากรายการที่เพิ่มอยู่ในชุดแล้วดังนั้นคุณสามารถลัดวงจรและมัดออกจากการเพิ่มใด ๆ ที่ส่งคืนfalseหากสิ่งที่คุณต้องการทราบคือมีรายการที่ซ้ำกันหรือไม่


13
ตรวจสอบให้แน่ใจว่าได้ติดตั้ง hashCode / equals ด้วย
jon077

1
หรือง่ายกว่านั้น: รวมไว้เมื่อสร้างชุดเช่น HashSet ใหม่ (รายการ) แทนการใช้ addAll
Fabian Steeg

2
@ jon077: ขึ้นอยู่กับคำจำกัดความของคำว่า "ซ้ำ"
Michael Myers

กระบวนการตรวจจับองค์ประกอบในอาร์เรย์ 2 มิติจะเหมือนกันหรือไม่? ตัวอย่างเช่นการตรวจสอบจาก array [0] [0] ถึง array [0] [6] (a 'row') .. ? ขอบคุณมาก Terry

แต่ละออบเจ็กต์ในอาร์เรย์เก็บค่าจำนวนเต็ม โดย "ซ้ำ" วัตถุจะมีค่าจำนวนเต็มเท่ากัน

60

ปรับปรุงโค้ดโดยใช้ค่าส่งคืนSet#addแทนการเปรียบเทียบขนาดของรายการและชุด

public static <T> boolean hasDuplicate(Iterable<T> all) {
    Set<T> set = new HashSet<T>();
    // Set#add returns false if the set does not change, which
    // indicates that a duplicate element has been added.
    for (T each: all) if (!set.add(each)) return true;
    return false;
}

7
การบอก HashSet จะมีประสิทธิภาพมากกว่าSet<T> set = new HashSet<T>(list.size());ไหมว่าจะจัดสรรพื้นที่เท่าไร: ? ด้วยพารามิเตอร์รายการฉันคิดว่ามันจะมีประสิทธิภาพมากกว่าหากเป็นเรื่องปกติที่รายการจะไม่มีรายการที่ซ้ำกัน
Paul Jackson

1
@PaulJackson การปรับขนาดตามรายการทั้งหมดอาจเป็นประโยชน์ อย่างไรก็ตามหากเป็นกรณีทั่วไปที่พบว่ามีรายการซ้ำในช่วงต้นก็จะเสียพื้นที่ไปโดยเปล่าประโยชน์ นอกจากนี้การปรับขนาดHashSetให้เท่ากับขนาดของรายการจะส่งผลให้มีการปรับขนาดเมื่อเรียกใช้รายการทั้งหมดเนื่องจากปัจจัยการโหลดพื้นฐานของโครงสร้างแฮช
Jay Anderson

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

15

หากคุณกำลังมองหาที่จะหลีกเลี่ยงการซ้ำกันที่ทั้งหมดแล้วคุณก็ควรจะตัดออกจากกระบวนการกลางของการตรวจสอบซ้ำกันและใช้ชุด


1
อย่าลืมใช้ hashCode / เท่ากับ :)
jon077

@ jon077: ไม่จำเป็นอย่างที่ฉันพูดไป
Michael Myers

1
อย่างไรก็ตามการใช้ชุดตรวจไม่พบรายการที่ซ้ำกัน มันแค่ป้องกันพวกเขา เว้นแต่คุณจะตรวจสอบผลลัพธ์ของวิธีการเพิ่มตามที่ระบุไว้โดย @akuhn ด้านบน
mcallahan

13

ปรับปรุงโค้ดเพื่อส่งคืนองค์ประกอบที่ซ้ำกัน

  • สามารถค้นหารายการที่ซ้ำกันในคอลเล็กชัน
  • ส่งคืนชุดของรายการที่ซ้ำกัน
  • สามารถรับองค์ประกอบเฉพาะได้จากชุด

public static <T> List getDuplicate(Collection<T> list) {

    final List<T> duplicatedObjects = new ArrayList<T>();
    Set<T> set = new HashSet<T>() {
    @Override
    public boolean add(T e) {
        if (contains(e)) {
            duplicatedObjects.add(e);
        }
        return super.add(e);
    }
    };
   for (T t : list) {
        set.add(t);
    }
    return duplicatedObjects;
}


public static <T> boolean hasDuplicate(Collection<T> list) {
    if (getDuplicate(list).isEmpty())
        return false;
    return true;
}

มันยอดเยี่ยมมาก คุณมีรหัสที่ไม่ถูกต้องและอาจจะไม่ใช่วิธีที่ดีที่สุด แต่แนวทางของคุณนั้นยอดเยี่ยมมาก! (และใช้งานได้ดี)
Jules Colle

9

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

ความซับซ้อนโดยรวมจะเป็น O (n log (n)) ซึ่งใกล้เคียงกับสิ่งที่คุณจะได้รับจาก Set (n คูณยาว (n)) แต่มีค่าคงที่น้อยกว่ามาก เนื่องจากค่าคงที่ในการเรียงลำดับ / ผลการหักลบจากต้นทุนของการเปรียบเทียบองค์ประกอบในขณะที่ต้นทุนจากชุดส่วนใหญ่มักจะเป็นผลมาจากการคำนวณแฮชบวกการเปรียบเทียบแฮชหนึ่งรายการ (อาจเป็นหลายรายการ) หากคุณกำลังใช้การใช้งาน Set ที่ใช้แฮชนั่นก็คือเนื่องจาก Tree based จะให้ O (n log² (n)) ซึ่งแย่กว่านั้น

ตามที่ฉันเข้าใจคุณไม่จำเป็นต้องลบรายการที่ซ้ำกันออกแต่เพียงแค่ทดสอบการมีอยู่ของพวกเขาเท่านั้น ดังนั้นคุณควรเขียนโค้ดอัลกอริธึมการจัดเรียงแบบผสานหรือฮีปด้วยมือบนอาร์เรย์ของคุณซึ่งเพียงแค่ออกจากการส่งคืนจริง (เช่น "มีการทับซ้อน") หากตัวเปรียบเทียบของคุณคืนค่า 0 และทำการเรียงลำดับให้เสร็จสมบูรณ์และสำรวจการทดสอบอาร์เรย์ที่เรียงลำดับสำหรับการทำซ้ำ . ในการเรียงลำดับการผสานหรือฮีปแน่นอนเมื่อการเรียงลำดับเสร็จสิ้นคุณจะต้องเปรียบเทียบคู่ที่ซ้ำกันทุกคู่เว้นแต่องค์ประกอบทั้งสองจะอยู่ในตำแหน่งสุดท้ายแล้ว (ซึ่งไม่น่าเป็นไปได้) ดังนั้นอัลกอริทึมการเรียงลำดับที่ปรับแต่งควรให้การปรับปรุงประสิทธิภาพอย่างมาก (ฉันจะต้องพิสูจน์ให้ได้ แต่ฉันเดาว่าอัลกอริทึมที่ปรับแต่งควรอยู่ใน O (log (n)) บนข้อมูลสุ่มที่สม่ำเสมอ)


ในกรณีนี้ n คือ 6 ดังนั้นฉันจะไม่เสียเวลากับรายละเอียดการใช้งานมากนัก แต่ฉันจะเก็บความคิดของคุณเกี่ยวกับการจัดเรียงฮีปพิเศษหากฉันต้องการทำอะไรแบบนั้น
Paul Tomblin

ฉันไม่เข้าใจย่อหน้าที่สาม Mergesort และ heapsort เป็นทั้ง O (nlog (n)) ไม่ใช่ O (log (n)) ตามที่คุณเขียน แม้ว่าคุณจะออกไปเมื่อคุณระบุว่าซ้ำกัน แต่ก็ยังไม่เปลี่ยนความซับซ้อนของเวลาของคุณ ...
ChaimKut

8

ฉันต้องการดำเนินการที่คล้ายกันสำหรับ a Streamแต่ไม่พบตัวอย่างที่ดี นี่คือสิ่งที่ฉันคิดขึ้นมา

public static <T> boolean areUnique(final Stream<T> stream) {
    final Set<T> seen = new HashSet<>();
    return stream.allMatch(seen::add);
}

สิ่งนี้มีข้อดีของการลัดวงจรเมื่อพบรายการที่ซ้ำกันก่อนแทนที่จะต้องประมวลผลสตรีมทั้งหมดและไม่ซับซ้อนมากไปกว่าการใส่ทุกอย่างลงใน a Setและตรวจสอบขนาด ดังนั้นกรณีนี้จะเป็น:

List<T> list = ...
boolean allDistinct = areUnique(list.stream());

7

ด้วย Java 8+ คุณสามารถใช้ Stream API:

boolean areAllDistinct(List<Block> blocksList) {
    return blocksList.stream().map(Block::getNum).distinct().count() == blockList.size();
}

1

ใส่เพียง: 1) ตรวจสอบให้แน่ใจว่ารายการทั้งหมดสามารถเทียบเคียงกันได้ 2) จัดเรียงอาร์เรย์ 2) วนซ้ำบนอาร์เรย์และค้นหารายการที่ซ้ำกัน


แพงเกินไปเมื่อพิจารณาถึงความซับซ้อนของอัลกอริทึม การเรียงลำดับที่ดีที่สุดคือ O (n * log (n)) ในขณะที่คุณสามารถค้นหารายการซ้ำที่มีความซับซ้อน O (n) ได้
Sergiy Dakhniy

1

หากต้องการทราบรายการที่ซ้ำกันในรายการให้ใช้รหัสต่อไปนี้: จะให้ชุดที่มีรายการที่ซ้ำกัน

 public Set<?> findDuplicatesInList(List<?> beanList) {
    System.out.println("findDuplicatesInList::"+beanList);
    Set<Object> duplicateRowSet=null;
    duplicateRowSet=new LinkedHashSet<Object>();
            for(int i=0;i<beanList.size();i++){
                Object superString=beanList.get(i);
                System.out.println("findDuplicatesInList::superString::"+superString);
                for(int j=0;j<beanList.size();j++){
                    if(i!=j){
                         Object subString=beanList.get(j);
                         System.out.println("findDuplicatesInList::subString::"+subString);
                         if(superString.equals(subString)){
                             duplicateRowSet.add(beanList.get(j));
                         }
                    }
                }
            }
            System.out.println("findDuplicatesInList::duplicationSet::"+duplicateRowSet);
        return duplicateRowSet;
  }

1

วิธีที่ดีที่สุดในการจัดการปัญหานี้คือการใช้HashSet :

ArrayList<String> listGroupCode = new ArrayList<>();
listGroupCode.add("A");
listGroupCode.add("A");
listGroupCode.add("B");
listGroupCode.add("C");
HashSet<String> set = new HashSet<>(listGroupCode);
ArrayList<String> result = new ArrayList<>(set);

เพียงพิมพ์รายการ อาร์เรย์ผลลัพธ์และดูผลลัพธ์โดยไม่ซ้ำกัน :)


1

หากคุณต้องการชุดของค่าที่ซ้ำกัน:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class FindDuplicateInArrayList {

    public static void main(String[] args) {

        Set<String> uniqueSet = new HashSet<String>();
        List<String> dupesList = new ArrayList<String>();
        for (String a : args) {
            if (uniqueSet.contains(a))
                dupesList.add(a);
            else
                uniqueSet.add(a);
        }
        System.out.println(uniqueSet.size() + " distinct words: " + uniqueSet);
        System.out.println(dupesList.size() + " dupesList words: " + dupesList);
    }
}

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


คำตอบที่ง่ายที่สุดและดีที่สุดหากคุณต้องการให้ซ้ำกันเพื่อประสิทธิภาพคุณอาจเริ่มคำใบ้ uniqueSet ด้วยขนาดของ args
Christophe Roussy

0
    String tempVal = null;
    for (int i = 0; i < l.size(); i++) {
        tempVal = l.get(i); //take the ith object out of list
        while (l.contains(tempVal)) {
            l.remove(tempVal); //remove all matching entries
        }
        l.add(tempVal); //at last add one entry
    }

หมายเหตุ: สิ่งนี้จะมีผลการดำเนินงานที่สำคัญแม้ว่ารายการจะถูกลบออกจากจุดเริ่มต้นของรายการ เพื่อแก้ไขปัญหานี้เรามีสองทางเลือก 1) วนซ้ำตามลำดับย้อนกลับและลบองค์ประกอบ 2) ใช้ LinkedList แทน ArrayList เนื่องจากคำถามที่เอนเอียงถามในการสัมภาษณ์เพื่อลบรายการที่ซ้ำกันออกจากรายการโดยไม่ต้องใช้คอลเล็กชันอื่น ๆ ตัวอย่างข้างต้นจึงเป็นคำตอบ ในโลกแห่งความเป็นจริงถ้าฉันต้องบรรลุสิ่งนี้ฉันจะใส่องค์ประกอบจาก List to Set ง่าย ๆ !


0
/**
     * Method to detect presence of duplicates in a generic list. 
     * Depends on the equals method of the concrete type. make sure to override it as required.
     */
    public static <T> boolean hasDuplicates(List<T> list){
        int count = list.size();
        T t1,t2;

        for(int i=0;i<count;i++){
            t1 = list.get(i);
            for(int j=i+1;j<count;j++){
                t2 = list.get(j);
                if(t2.equals(t1)){
                    return true;
                }
            }
        }
        return false;
    }

ตัวอย่างของคลาสคอนกรีตที่ลบล้างequals():

public class Reminder{
    private long id;
    private int hour;
    private int minute;

    public Reminder(long id, int hour, int minute){
        this.id = id;
        this.hour = hour;
        this.minute = minute;
    }

    @Override
    public boolean equals(Object other){
        if(other == null) return false;
        if(this.getClass() != other.getClass()) return false;
        Reminder otherReminder = (Reminder) other;
        if(this.hour != otherReminder.hour) return false;
        if(this.minute != otherReminder.minute) return false;

        return true;
    }
}

0
    ArrayList<String> withDuplicates = new ArrayList<>();
    withDuplicates.add("1");
    withDuplicates.add("2");
    withDuplicates.add("1");
    withDuplicates.add("3");
    HashSet<String> set = new HashSet<>(withDuplicates);
    ArrayList<String> withoutDupicates = new ArrayList<>(set);

    ArrayList<String> duplicates = new ArrayList<String>();

    Iterator<String> dupIter = withDuplicates.iterator();
    while(dupIter.hasNext())
    {
    String dupWord = dupIter.next();
    if(withDuplicates.contains(dupWord))
    {
        duplicates.add(dupWord);
    }else{
        withoutDupicates.add(dupWord);
    }
    }
  System.out.println(duplicates);
  System.out.println(withoutDupicates);

เพิ่มคำอธิบายพร้อมคำตอบว่าคำตอบนี้ช่วย OP ในการแก้ไขปัญหาปัจจุบันได้อย่างไร
ρяσѕρєя K

0

คำตอบนี้เขียนใน Kotlin แต่สามารถแปลเป็น Java ได้อย่างง่ายดาย

หากขนาดของอาร์เรย์ของคุณอยู่ในช่วงขนาดเล็กคงที่นี่เป็นทางออกที่ดี

var duplicateDetected = false
    if(arrList.size > 1){
        for(i in 0 until arrList.size){
            for(j in 0 until arrList.size){
                if(i != j && arrList.get(i) == arrList.get(j)){
                    duplicateDetected = true
                }
            }
        }
    }

0
private boolean isDuplicate() {
    for (int i = 0; i < arrayList.size(); i++) {
        for (int j = i + 1; j < arrayList.size(); j++) {
            if (arrayList.get(i).getName().trim().equalsIgnoreCase(arrayList.get(j).getName().trim())) {
                return true;
            }
        }
    }

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