Java 8'deki mülklere göre bir nesne listesinden çiftleri kaldırın

Bazı özelliklerine göre bir nesne listesinden çiftleri kaldırmaya çalışıyorum.

java 8'i kullanarak basit bir şekilde yapabilir miyiz?

List employee

Çalışanlardan kimlik mülküne dayanarak kopyaları kaldırabilir miyiz? Dizi dizilerini kaldırarak dizeleri arraylist olarak gördüm.

36
Yalnızca Çalışanlar eşittir ve hashCode 'ı doğru olarak çiftleri doğru şekilde tanımlayacak şekilde uygulayan @Ranjeet.
katma yazar Madbreaks, kaynak
Neden bunun için Listeyi kullanıyorsunuz? Listenin yerine Set'i kullanın.
katma yazar Ranjeet, kaynak
employee.name kopyasını aramak mı istiyorsunuz? veya amacınız nedir, lütfen daha fazla bilgi verin
katma yazar Dude, kaynak

9 cevap

List sayfasından bir akış alabilir ve kimliğinizi benzersiz bir şekilde karşılaştıran bir özel karşılaştırıcı sağladığınız TreeSet 'a ekleyebilirsiniz.

Sonra gerçekten bir listeye ihtiyacınız varsa, bu koleksiyonu ArrayList'e geri koyabilirsiniz.

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

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

Örnek verildiğinde:

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

Çıkacak:

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

Başka bir fikir, bir çalışanı saran ve onun kimliğine göre eşittir ve hashcode yöntemine sahip bir sarıcı kullanmak olabilir:

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

Ardından, her örneği kaydırın, distinct() numaralı telefonu arayın, bunları paketlerinden çıkarın ve sonucu bir liste halinde toplayın.

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

Aslında, bu sarmalayıcıyı, karşılaştırmayı yapacak bir işlev sağlayarak jenerik yapabileceğinizi düşünüyorum:

class Wrapper {
    private T t;
    private Function equalityFunction;

    public Wrapper(T t, Function 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 that = (Wrapper) o;
        return Objects.equals(equalityFunction.apply(this.t), that.equalityFunction.apply(that.t));
    }

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

ve haritalama şöyle olacaktır:

.map(e -> new Wrapper<>(e, Employee::getId))
63
katma
Çok teşekkür ederim
katma yazar Patan, kaynak
Bu şekilde uyguladım, ancak bazı durumlarda sınama durumlarını çalıştırırken boş gösterici istisnası alıyorum. Tuhaf olan şey, test senaryolarını çalıştırdığımda, hata gider. Bunun neden olduğu hakkında bir fikrin var mı? Bu konuda bana yardımcı olursan harika olur.
katma yazar Patan, kaynak
İlk örnek benim için çalışıyor ama anlamıyorum neden :)
katma yazar Bevor, kaynak
@Patan Hard, test senaryoları olmadan söylemek gerekirse listede hiç null referans bulunmadığını kontrol edin.
katma yazar Alexis C., kaynak
@Bevor Bir karşılaştırıcıyı parametre olarak alan set kurucuyu kullanır. Örnekte, aynı kimliğe sahip her çalışan eşit kabul edilir ve sonuçta ortaya çıkan kümede benzersiz olacaktır.
katma yazar Alexis C., kaynak
İlk öneriniz, sarıcıdan çok daha iyi bir cevaptır :). Sarıcı belli, ama ilki daha iyi. ToplamaAndThen farkında değildi
katma yazar Jatin, kaynak

Bunu doğrudan listede yapmanın en kolay yolu

HashSet seen=new HashSet<>();
employee.removeIf(e->!seen.add(e.getID()));
  • removeIf will remove an element if it meets the specified criteria
  • Set.add will return false if it did not modify the Set, i.e. already contains the value
  • combining these two, it will remove all elements (employees) whose id has been encountered before

Tabii ki, sadece liste öğelerin kaldırılmasını destekliyorsa çalışır.

29
katma
@ user3871754: Kompozit anahtarı tutan ve uygun eşittir ve hashCode uygulamalarına sahip bir nesneye ihtiyacınız vardır. yourList.removeIf (e ->! seen.add (Arrays.asList (e.getFirstKeyPart (), e.getSecondKeyPart ())) Arrays.asList Kod>, az sayıda bileşen için isteğe bağlı sayıda bileşenle çalışır, buna karşın özel bir anahtar türü daha verimli olabilir.
katma yazar Holger, kaynak
Bu kimliğin benzersiz olduğunu varsayalım, bileşik anahtarım varsa
katma yazar user3871754, kaynak

Bu kodu deneyin:

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

Eşittir 'i kullanabilirseniz, listeyi bir akıştaki farklı kullanarak filtreleyin (yukarıdaki yanıtlara bakın). Eşittir yöntemini geçersiz kılmak veya yapamazsanız, herhangi bir özellik için aşağıdaki şekilde akışı filtreleyebilirsiniz . Mülkiyet Adı için (Mülk Kimliği için aynıdır):

Set nameSet = new HashSet<>();
List employeesDistinctByName = employees.stream()
            .filter(e -> nameSet.add(e.getName()))
            .collect(Collectors.toList());
2
katma
Bu oldukça iyiydi, her bir elemanı bir yüklemi temel alarak filtrelemeye veya sürdürmeye karar veren filtrenin basit işlevselliğinden yararlanır (özellik dahilinde bulunup bulunmayacağını belirlemek için her bir öğeye uygulanmasını önerir), (String türü) Bir kümeye ekleme: yeni takılırsa true, eğer varsa zaten yanlış ... o akıllıydı! benim için harika çalış!
katma yazar Shessuky, kaynak

Sipariş önemli değilse ve paralel olarak daha fazla performans gösterdiğinde, bir Haritaya toplayın ve ardından değerleri alın:

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

Bu benim için çalıştı:

list.stream().distinct().collect(Collectors.toList());
1
katma
Tabi ki eşitliği uygulaman gerekiyor.
katma yazar sebadagostino, kaynak

Basit bir başka versiyonu

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

TreeSet outputList = appendTree.apply(new TreeSet(Comparator.comparing(p->p.getId())),personList);
0
katma
Bu, TreeSet <�Çalışan> outputList = new TreeSet <> (Comparator.comparing (p-> p.getId ())) 'ın örtülü bir sürümüdür; outputList.addAll (personList); Düz ileri kod çok daha basit.
katma yazar Holger, kaynak

Başka bir çözüm bir Öngörün kullanmaktır, bunu herhangi bir filtrede kullanabilirsiniz:

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

Ardından, yüklemeyi herhangi bir yerde yeniden kullanın:

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

Not: JavaToc filtresinde, durum bilgisi olmayan bir Predicte olduğunu söyler. Aslında, akış paralel olsa bile bu iyi çalışır.


About other solutions:

1) .collect (Collectors.toConcurrentMap (..)) değerini kullanın.() iyi bir çözümdür, ancak sıralamayı sıralamak ve saklamak istiyorsanız can sıkıcıdır.

2) stream.removeIf(e->!seen.add(e.getID())); is also another very good solution. But we need to make sure the collection implemented removeIf, for example it will throw exception if we construct the collection use Arrays.asList(..).

0
katma

Burada çok iyi cevaplar var ama küçült metodunu kullanmadım. Yani sizin durumunuz için, aşağıdaki şekilde uygulayabilirsiniz:

 List employeeList = employees.stream()
      .reduce(new ArrayList<>(), (List 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
katma
Paralel Akımlar ile çalışmak, birleştiricinin tekrar tekrar aynı kimlikle çalışanları bir araya getirme şansı vardır. Bu durumda çiftleri kontrol etmelisiniz.
katma yazar Sven Dhaens, kaynak