Pertanyaan Hapus duplikat dari daftar objek berdasarkan properti di Java 8


Saya mencoba menghapus duplikat dari Daftar objek berdasarkan beberapa properti.

bisakah kita melakukannya dengan cara sederhana menggunakan java 8

List<Employee> employee

Bisakah kita menghapus duplikat dari itu berdasarkan id milik karyawan. Saya telah melihat posting menghapus string duplikat dari arraylist string.


36
2018-04-16 09:07


asal


Jawaban:


Anda bisa mendapatkan aliran dari List dan dimasukkan ke dalam TreeSet dari mana Anda menyediakan pembanding khusus yang membandingkan id secara unik.

Kemudian jika Anda benar-benar membutuhkan daftar Anda dapat menempatkan kembali koleksi ini ke ArrayList.

import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;

...
List<Employee> unique = employee.stream()
                                .collect(collectingAndThen(toCollection(() -> new TreeSet<>(comparingInt(Employee::getId))),
                                                           ArrayList::new));

Diberikan contoh:

List<Employee> employee = Arrays.asList(new Employee(1, "John"), new Employee(1, "Bob"), new Employee(2, "Alice"));

Ini akan menampilkan:

[Employee{id=1, name='John'}, Employee{id=2, name='Alice'}]

Ide lain bisa menggunakan pembungkus yang membungkus karyawan dan memiliki metode yang sama dan metode hashcode dengan idnya:

class WrapperEmployee {
    private Employee e;

    public WrapperEmployee(Employee e) {
        this.e = e;
    }

    public Employee unwrap() {
        return this.e;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        WrapperEmployee that = (WrapperEmployee) o;
        return Objects.equals(e.getId(), that.e.getId());
    }

    @Override
    public int hashCode() {
        return Objects.hash(e.getId());
    }
}

Kemudian Anda membungkus setiap instance, panggil distinct(), buka bungkusnya dan kumpulkan hasilnya dalam daftar.

List<Employee> unique = employee.stream()
                                .map(WrapperEmployee::new)
                                .distinct()
                                .map(WrapperEmployee::unwrap)
                                .collect(toList());

Bahkan, saya pikir Anda dapat membuat pembungkus ini generik dengan menyediakan fungsi yang akan melakukan perbandingan:

class Wrapper<T, U> {
    private T t;
    private Function<T, U> equalityFunction;

    public Wrapper(T t, Function<T, U> equalityFunction) {
        this.t = t;
        this.equalityFunction = equalityFunction;
    }

    public T unwrap() {
        return this.t;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        @SuppressWarnings("unchecked")
        Wrapper<T, U> that = (Wrapper<T, U>) o;
        return Objects.equals(equalityFunction.apply(this.t), that.equalityFunction.apply(that.t));
    }

    @Override
    public int hashCode() {
        return Objects.hash(equalityFunction.apply(this.t));
    }
}

dan pemetaannya adalah:

.map(e -> new Wrapper<>(e, Employee::getId))

61
2018-04-16 10:07



Cara termudah untuk melakukannya langsung di daftar adalah

HashSet<Object> seen=new HashSet<>();
employee.removeIf(e->!seen.add(e.getID()));
  • removeIf akan menghapus elemen jika memenuhi kriteria yang ditentukan
  • Set.add akan kembali false jika tidak memodifikasi Set, yaitu sudah mengandung nilainya
  • menggabungkan keduanya, itu akan menghapus semua elemen (karyawan) yang idnya telah ditemukan sebelumnya

Tentu saja, ini hanya berfungsi jika daftar mendukung penghapusan elemen.


27
2018-04-16 10:50



Coba kode ini:

Collection<Employee> nonDuplicatedEmployees = employees.stream()
   .<Map<Integer, Employee>> collect(HashMap::new,(m,e)->m.put(e.getId(), e), Map::putAll)
   .values();

9
2018-04-16 09:51



Jika urutan tidak penting dan ketika itu lebih berkinerja untuk berjalan secara paralel, Kumpulkan ke Peta dan kemudian dapatkan nilainya:

employee.stream().collect(Collectors.toConcurrentMap(Employee::getId, Function.identity(), (p, q) -> p)).values()

2
2018-05-24 03:30



Jika Anda bisa memanfaatkan equals, lalu filter daftar dengan menggunakan distinct dalam aliran (lihat jawaban di atas). Jika Anda tidak bisa atau tidak ingin mengganti equals metode, Anda bisa filter aliran dengan cara berikut untuk properti apa pun, mis. untuk Nama properti (sama untuk ID properti dll.):

Set<String> nameSet = new HashSet<>();
List<Employee> employeesDistinctByName = employees.stream()
            .filter(e -> nameSet.add(e.getName()))
            .collect(Collectors.toList());

2
2018-04-10 03:18



Ini berhasil untuk saya:

list.stream().distinct().collect(Collectors.toList());

1
2018-03-07 13:00



Versi lain yang sederhana

BiFunction<TreeSet<Employee>,List<Employee> ,TreeSet<Employee>> appendTree = (y,x) -> (y.addAll(x))? y:y;

TreeSet<Employee> outputList = appendTree.apply(new TreeSet<Employee>(Comparator.comparing(p->p.getId())),personList);

0
2018-04-16 18:58



Ada banyak jawaban yang bagus di sini tetapi saya tidak menemukan satu tentang penggunaan reduce metode. Jadi untuk kasus Anda, Anda dapat menerapkannya dengan cara berikut:

 List<Employee> employeeList = employees.stream()
      .reduce(new ArrayList<>(), (List<Employee> accumulator, Employee employee) ->
      {
        if (accumulator.stream().noneMatch(emp -> emp.getId().equals(employee.getId())))
        {
          accumulator.add(employee);
        }
        return accumulator;
      }, (acc1, acc2) ->
      {
        acc1.addAll(acc2);
        return acc1;
      });

0
2018-06-27 10:12



Solusi lain adalah menggunakan Predikat, lalu Anda dapat menggunakan ini dalam filter apa pun:

public static <T> Predicate<T> distinctBy(Function<? super T, ?> f) {
  Set<Object> objects = new ConcurrentHashSet<>();
  return t -> objects.add(f.apply(t));
}

Kemudian cukup gunakan kembali predikatnya di mana saja:

employees.stream().filter(distinctBy(e -> e.getId));

Catatan: di JavaDoc filter, yang mengatakan dibutuhkan Predicte tanpa negara. Sebenarnya, ini berfungsi dengan baik bahkan jika alirannya paralel.


Tentang solusi lain:

1) Menggunakan .collect(Collectors.toConcurrentMap(..)).values() adalah solusi yang baik, tetapi itu menjengkelkan jika Anda ingin mengurutkan dan menyimpan pesanan.

2) stream.removeIf(e->!seen.add(e.getID())); juga solusi lain yang sangat bagus. Tetapi kita perlu memastikan koleksi tersebut diimplementasikan, jika tidak, misalnya akan membuang pengecualian jika kita membangun penggunaan koleksi Arrays.asList(..).


0
2017-07-14 18:21