Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Java

Java: ลบ Duplicate Elements จาก List

วิธีการลบค่าซ้ำ (duplicates) จาก List ใน Java มีหลายวิธี ขึ้นอยู่กับ Java version และ requirement (preserve order หรือไม่)

วิธีที่ 1: ใช้ LinkedHashSet (Java 1.4+) - Preserve Order

แนะนำสำหรับ general use - รักษาลำดับการ insert

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

public class Demo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("Jacob");
        list.add("Gary");
        list.add("Gary");    // duplicate
        list.add("Harry");
        list.add("Harry");   // duplicate
        list.add("Kevin");

        System.out.println("Original List = " + list);

        // LinkedHashSet รักษาลำดับ insertion order
        Set<String> set = new LinkedHashSet<>(list);
        
        System.out.println("After removing duplicates = " + set);
        // [Jacob, Gary, Harry, Kevin]
    }
}

Output

Original List = [Jacob, Gary, Gary, Harry, Harry, Kevin]
After removing duplicates = [Jacob, Gary, Harry, Kevin]

วิธีที่ 2: ใช้ Stream.distinct() (Java 8+) - Preserve Order

Functional style, ใช้กับ List ใดๆ ได้

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

public class Demo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList(
            "Ryan", "Kevin", "Ryan", "Harry", "Mark", "Mark"
        ));

        System.out.println("Original ArrayList = " + list);

        List<String> distinctList = list.stream()
            .distinct()
            .collect(Collectors.toList());

        System.out.println("Updated ArrayList without duplicates = " + distinctList);
        // [Ryan, Kevin, Harry, Mark]
    }
}

Output

Original ArrayList = [Ryan, Kevin, Ryan, Harry, Mark, Mark]
Updated ArrayList without duplicates = [Ryan, Kevin, Harry, Mark]

วิธีที่ 3: ใช้ HashSet (ไม่รักษาลำดับ) - Fastest

import java.util.*;

public class HashSetDemo {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("A", "B", "A", "C", "B");
        
        // ไม่รักษาลำดับ แต่เร็วกว่า
        Set<String> set = new HashSet<>(list);
        List<String> unique = new ArrayList<>(set);
        
        System.out.println(unique); // ลำดับไม่คงที่ เช่น [A, B, C]
    }
}

วิธีที่ 4: In-place Removal (ไม่สร้าง List ใหม่)

import java.util.*;

public class InPlaceDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>(Arrays.asList("A", "B", "A", "C", "B"));
        
        // ใช้ Iterator เพื่อหลีกเลี่ยง ConcurrentModificationException
        Set<String> seen = new HashSet<>();
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String s = it.next();
            if (!seen.add(s)) { // add คืน false ถ้ามีอยู่แล้ว
                it.remove();
            }
        }
        
        System.out.println(list); // [A, B, C] - รักษาลำดับ first occurrence
    }
}

วิธีที่ 5: ใช้ TreeSet (Sorted + Unique)

import java.util.*;

public class TreeSetDemo {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(5, 2, 8, 2, 5, 1, 9);
        
        // Sorted + Unique
        Set<Integer> sortedUnique = new TreeSet<>(list);
        System.out.println(sortedUnique); // [1, 2, 5, 8, 9]
    }
}

สำหรับ Custom Objects - ต้อง Implement equals() และ hashCode()

import java.util.*;

class Person {
    String name;
    int age;
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @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 && Objects.equals(name, p.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    
    @Override
    public String toString() {
        return name + "(" + age + ")";
    }
}

public class CustomObjectDemo {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("Alice", 25),
            new Person("Bob", 30),
            new Person("Alice", 25), // duplicate
            new Person("Charlie", 25)
        );
        
        List<Person> unique = people.stream()
            .distinct() // ใช้ equals/hashCode
            .collect(Collectors.toList());
        
        System.out.println(unique); // [Alice(25), Bob(30), Charlie(25)]
    }
}

ตารางเปรียบเทียบวิธีต่างๆ

วิธี Preserve Order Null Safe Performance Java Version
LinkedHashSet ✅ ใช่ ✅ ใช่ O(n) 1.4+
Stream.distinct() ✅ ใช่ ✅ ใช่ O(n) 8+
HashSet ❌ ไม่ ✅ ใช่ O(n) เร็วที่สุด 1.2+
TreeSet ✅ Sorted ❌ ไม่ (null ไม่ได้) O(n log n) 1.2+
In-place Iterator ✅ ใช่ ✅ ใช่ O(n) 1.2+

ข้อควรระวัง

  • Mutable objects: หาก object เปลี่ยนค่าหลัง add เข้า Set อาจทำให้ hashCode เปลี่ยน → duplicate ไม่ถูกลบ
  • Null elements: HashSet, LinkedHashSet รองรับ null (1 ตัว) แต่ TreeSet ไม่รองรับ
  • Performance: สำหรับ list ใหญ่มาก พิจารณา Stream.parallel() หรือ database distinct