자바 : ArrayList에서 중복을 감지 하시겠습니까?


104

ArrayList에 Java에서 동일한 요소가 두 개 이상 포함되어 있는지 감지 (참 / 거짓 반환)하려면 어떻게해야합니까?

감사합니다, 테리

편집 "블록"을 서로 비교하는 것이 아니라 정수 값을 비교하는 것을 잊었습니다. 각 "블록"에는 int가 있으며 이것이 다른 점입니다. "getNum"이라는 메서드를 호출하여 특정 Block의 정수를 찾습니다 (예 : table1 [0] [2] .getNum ();


"Block"이 int로 비교되는 경우 hashCode가 동일한 int를 반환하고 동일한 int를 비교해야합니다.
Paul Tomblin

대신 목록의 사용 설정
dmarquina

답변:


192

가장 간단한 방법 : 전체 컬렉션을 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 블록 배열이 있습니다.

블록 테이블 [] [];

중복 된 행이 있는지 감지하고 싶습니까?

이 경우 Block이 "equals"및 "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를 반환 false하므로 중복 항목이 있는지 여부 만 알고 싶은 경우 반환되는 추가 항목을 단락하고 베일 아웃 할 수도 있습니다 .


13
hashCode / equals도 구현해야합니다.
jon077

1
또는 조금 더 쉽습니다 : addAll을 사용하는 대신 새 HashSet (list)와 같이 집합을 만들 때 래핑합니다.
Fabian Steeg

2
@ jon077 : "중복"의 정의에 따라 다릅니다.
Michael Myers

2D 배열에서 요소를 감지하는 프로세스가 동일합니까? 예를 들어, array [0] [0]에서 array [0] [6] ( '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());일까요? List 매개 변수가 주어지면 목록에 중복 항목이 포함되지 않는 것이 일반적이면 더 효율적이라고 생각합니다.
Paul Jackson

1
전체 목록을 기반으로 한 @PaulJackson 크기 조정이 아마도 도움이 될 것입니다. 그러나 일반적인 경우가 중복을 조기에 찾는 경우 공간이 낭비되었습니다. 또한 HashSet목록의 크기로 크기를 조정하더라도 해시 구조의 기본로드 요소로 인해 전체 목록을 실행할 때 크기가 조정됩니다.
Jay Anderson

1
런타임이나 공간에 실제 문제가 발생하지 않는 한 그런 코드를 미세 조정하지 않을 것입니다. 조기 최적화는 피하는 것이 가장 좋습니다.
akuhn

15

중복을 아예 피하려면 중복을 감지하는 중간 프로세스를 잘라 내고 Set을 사용해야합니다 .


1
hashCode / equals 구현 확인 :)
jon077

@ jon077 : 내가 방금 말했듯이 반드시 그런 것은 아닙니다.
Michael Myers

1
그러나 세트를 사용하면 중복을 감지 하지 못합니다 . 그것은 단지 그들을 막습니다. 물론 위의 @akuhn에서 언급 한대로 add 메서드의 결과를 확인하지 않는 한.
mcallahan 2017-10-20

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 log ( n))) 그런 다음 단일 패스를 수행하고 반복되는 항목을 찾습니다. 요소 (즉, 서로 뒤 따르는 동일한 요소)를 (이것은 O (n)).

전체적인 복잡성은 O (n log (n))가 될 것입니다. 이것은 Set (n 배 long (n))로 얻을 수있는 것과 거의 동일하지만 훨씬 더 작은 상수를 사용합니다. 이는 정렬 / 중복의 상수가 요소 비교 비용에서 발생하는 반면 집합의 비용은 해시 계산과 하나 (여러 개)의 해시 비교로 인해 발생할 가능성이 가장 높기 때문입니다. 해시 기반 세트 구현을 사용하는 경우, 즉 트리 기반이 O (n log² (n))를 제공하기 때문에 더 나쁩니다.

그러나 내가 이해했듯이 중복 을 제거 할 필요는 없으며 단지 존재 여부를 테스트하기 만하면됩니다. 따라서 배열에 병합 또는 힙 정렬 알고리즘을 직접 코딩해야합니다.이 알고리즘은 비교기가 0을 반환하면 true (예 : "there is a dup")를 반환하고 그렇지 않으면 정렬을 완료하고 정렬 된 배열 테스트를 반복합니다. . 병합 또는 힙 정렬에서 실제로 정렬이 완료되면 두 요소가 이미 최종 위치에 있지 않는 한 모든 중복 쌍을 비교하게됩니다 (가능성이 낮음). 따라서 조정 된 정렬 알고리즘은 엄청난 성능 향상을 가져올 것입니다 (그것을 증명해야하지만 조정 된 알고리즘은 균일하게 임의의 데이터에 대해 O (log (n))에 있어야한다고 생각합니다)


이 경우 n은 6이므로 구현 세부 정보에 많은 시간을 낭비하지 않을 것입니다.하지만 이와 같은 작업을 수행해야하는 경우 특수 힙 정렬에 대한 아이디어는 계속 유지하겠습니다.
Paul Tomblin

세 번째 단락을 이해하지 못합니다. Mergesort와 heapsort는 둘 다 O (nlog (n))이지 O (log (n))가 아닙니다. 중복을 확인한 후 종료하더라도 시간 복잡성은 변경되지 않습니다.
ChaimKut

8

에 대해 유사한 작업을 수행해야 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();
}

2

간단히 말해 : 1) 모든 항목이 비교 가능한지 확인하십시오. 2) 배열을 정렬하십시오. 2) 배열을 반복하고 중복 항목을 찾으십시오.


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);

결과 arraylist를 인쇄 하고 중복없이 결과를 확인하십시오. :)


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);
    }
}

또한 경우에 따라 값을 자르거나 소문자를 사용하는 것도 고려해보십시오.


중복을 원하는 경우 가장 간단하고 가장 좋은 대답은 성능을 위해 args 크기로 uniqueSet 힌트를 초기화 할 수 있습니다.
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) ArrayList 대신 LinkedList를 사용하십시오. 다른 컬렉션을 사용하지 않고 목록에서 중복 항목을 제거하기 위해 인터뷰에서 요청 된 편향된 질문으로 인해 위의 예가 답입니다. 하지만 현실 세계에서는 이것을 달성해야한다면 List에서 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로 쉽게 번역 할 수 있습니다.

arraylist의 크기가 고정 된 작은 범위 내에 있다면 이것은 훌륭한 솔루션입니다.

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.