Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Java

Java: Xóa phần tử trùng lặp khỏi List

Có nhiều cách để loại bỏ phần tử trùng lặp (duplicate) khỏi List trong Java, tùy thuộc vào yêu cầu: giữ thứ tự, hiệu năng, phiên bản Java.

Cách 1: LinkedHashSet (Giữ thứ tự, Java 1.4+)

Khuyên dùng cho hầu hết trường hợp. LinkedHashSet giữ thứ tự chèn (insertion order) và loại trùng lặp O(1) average.

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

public class Demo {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add("Jacob");
        list.add("Gary");
        list.add("Gary");
        list.add("Harry");
        list.add("Harry");
        list.add("Kevin");
        
        System.out.println("List gốc: " + list);
        
        // Chuyển sang LinkedHashSet -> loại trùng, giữ thứ tự
        Set set = new LinkedHashSet<>(list);
        
        // Chuyển lại List nếu cần
        List uniqueList = new ArrayList<>(set);
        
        System.out.println("Sau khi xóa trùng: " + uniqueList);
    }
}

Kết quả

List gốc: [Jacob, Gary, Gary, Harry, Harry, Kevin]
Sau khi xóa trùng: [Jacob, Gary, Harry, Kevin]

Cách 2: Stream API distinct() (Java 8+)

Ngắn gọn, functional style, giữ thứ tự.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Demo {
    public static void main(String[] args) {
        List list = Arrays.asList("Ryan", "Kevin", "Ryan", "Harry", "Mark", "Mark");
        
        System.out.println("ArrayList gốc: " + list);
        
        List distinct = list.stream()
                                    .distinct()
                                    .collect(Collectors.toList());
        
        System.out.println("Không trùng lặp: " + distinct);
    }
}

Kết quả

ArrayList gốc: [Ryan, Kevin, Ryan, Harry, Mark, Mark]
Không trùng lặp: [Ryan, Kevin, Harry, Mark]

Cách 3: HashSet (Không giữ thứ tự, nhanh nhất)

Set set = new HashSet<>(list);
List unique = new ArrayList<>(set); // Thứ tự ngẫu nhiên

Cách 4: Duyệt thủ công (Java cũ, không cần Collection)

List unique = new ArrayList<>();
for (String s : list) {
    if (!unique.contains(s)) {
        unique.add(s);
    }
}
// O(n^2) - chỉ dùng list rất nhỏ

Cách 5: Xóa trùng lặp TRONG LIST (in-place, Java 8+)

// Sử dụng LinkedHashSet để lọc, sau đó clear và addAll
Set seen = new LinkedHashSet<>(list);
list.clear();
list.addAll(seen);

So sánh các cách

CáchGiữ thứ tự?Độ phức tạpJava versionGhi chú
LinkedHashSetCóO(n)1.4+Khuyên dùng chung
Stream.distinct()CóO(n)8+Đẹp, functional
HashSetKhôngO(n)1.2+Nhanh nhất, không care order
Manual containsCóO(n²)1.0+Chỉ list < 100 phần tử
TreeSetSorted (natural order)O(n log n)1.2+Muốn kết quả sorted

Lưu ý cho Object tùy chỉnh

Để distinct() / Set hoạt động đúng với class tự định nghĩa, phải override equals() và hashCode().

public class Person {
    String name; int age;
    // constructor, getter...
    
    @Override public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person p = (Person) o;
        return age == p.age && name.equals(p.name);
    }
    @Override public int hashCode() {
        return Objects.hash(name, age);
    }
}

Xóa trùng theo key cụ thể (Java 8+)

// Giữ person đầu tiên cho mỗi name
List uniqueByName = list.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getName))),
        ArrayList::new
    ));

// Hoặc dùng Map (key = name, value = person đầu tiên)
List unique = list.stream()
    .collect(Collectors.toMap(Person::getName, p -> p, (a, b) -> a))
    .values().stream().toList(); // Java 16+ .toList()