2016-03-24 15 views
10

list B listesindeki tüm öğeleri listeA listesine ekleyin.java 8 ListB öğesinin tüm öğelerini ListA'ya birleştirin

listeA listesinde bir öğe zaten varsa (özel bir eşitlik denetimine bağlı olarak) eklemek istemiyorum.

Ben Set kullanmak istemiyorum, ben() ve hashCode() eşittir geçersiz kılmak istemiyoruz.

Nedenleri Ben sadece eşit olmayı düşünür Lista öğeler zaten varsa ListB dan birleştirmezseniz istiyorum, listA başına çiftleri önlemek istemiyorum vardır.

Eşittir() ve hashCode() öğesini geçersiz kılmak istemiyorum çünkü bu, öğelerin her koşulda tuttuğuna dair eşittir() öğemi uyguladığımdan emin olmak zorundayım. Bununla birlikte, listB'den gelen elemanların tam olarak başlatılmadığı, yani, listA öğelerinde var olabilecekleri bir nesne kimliğini kaçırabilecekleri de olabilir.

Benim şu anki yaklaşım bir arayüz ve Fayda-Fonksiyonu içerir:

public interface HasEqualityFunction<T> { 

    public boolean hasEqualData(T other); 
} 

public class AppleVariety implements HasEqualityFunction<AppleVariety> { 
    private String manufacturerName; 
    private String varietyName; 

    @Override 
    public boolean hasEqualData(AppleVariety other) { 
     return (this.manufacturerName.equals(other.getManufacturerName()) 
      && this.varietyName.equals(other.getVarietyName())); 
    } 

    // ... getter-Methods here 
} 


public class CollectionUtils { 
    public static <T extends HasEqualityFunction> void merge(
     List<T> listA, 
     List<T> listB) { 
     if (listB.isEmpty()) { 
      return; 
     } 
     Predicate<T> exists 
      = (T x) -> { 
       return listA.stream().noneMatch(
         x::hasEqualData); 
      }; 
     listA.addAll(listB.stream() 
      .filter(exists) 
      .collect(Collectors.toList()) 
     ); 
    } 
} 

Sonra bu gibi kullanmak istiyorum:

... 
List<AppleVariety> appleVarietiesFromOnePlace = ... init here with some elements 
List<AppleVariety> appleVarietiesFromAnotherPlace = ... init here with some elements 
CollectionUtils.merge(appleVarietiesFromOnePlace, appleVarietiesFromAnotherPlace); 
... 

tüm unsurları ile Lista benim yeni listesini almak için B'den birleşti

bu iyi bir yaklaşım var mı? Bunu başarmanın daha iyi/daha kolay bir yolu var mı? Bir HasEqualityFunction arayüzü gerekmez

public static <T> void merge(List<T> listA, List<T> listB, BiPredicate<T, T> areEqual) { 
    listA.addAll(listB.stream() 
         .filter(t -> listA.stream().noneMatch(u -> areEqual.test(t, u))) 
         .collect(Collectors.toList()) 
    ); 
} 

:

cevap

7

Böyle bir şey istiyorum. İki nesnenin mantığınıza göre eşit olup olmadığını sınamak için BiPredicate'u yeniden kullanabilirsiniz.

Bu kod, yalnızca listB numaralı listA numaralı belgede belirtilen yüklemelere göre öğeleri filtreler. listB'da elemanlar olduğu kadar listA'a kadar geçiş yapar.

public static <T> void merge(List<T> listA, List<T> listB, BiPredicate<T, T> areEqual, ToIntFunction<T> hashFunction) { 

    class Wrapper { 
     final T wrapped; 
     Wrapper(T wrapped) { 
      this.wrapped = wrapped; 
     } 
     @Override 
     public boolean equals(Object obj) { 
      return areEqual.test(wrapped, ((Wrapper) obj).wrapped); 
     } 
     @Override 
     public int hashCode() { 
      return hashFunction.applyAsInt(wrapped); 
     } 
    } 

    Set<Wrapper> wrapSet = listA.stream().map(Wrapper::new).collect(Collectors.toSet()); 

    listA.addAll(listB.stream() 
         .filter(t -> !wrapSet.contains(new Wrapper(t))) 
         .collect(Collectors.toList()) 
    ); 
} 

Bu ilk sarar her eleman bir Wrapper nesnesinin içinde ve toplar:


alternatif ve daha iyi ölçülebilir uygulama sizin unsurları sarar ve sahip equals yöntemi olarak yüklemi bir sarmalayıcı sınıf kullanmak olacaktır Onları bir Set içine. Ardından, bu sette bulunmayan listB öğelerini filtreler. Eşitlik testi, verilen yüklemi gerçekleştirerek yapılır. Kısıtlama, hashCode'u doğru bir şekilde uygulamak için hashFunction vermemiz gerektiğidir.

Örnek kod şöyle olacaktır:

List<String> listA = new ArrayList<>(Arrays.asList("foo", "bar", "test")); 
List<String> listB = new ArrayList<>(Arrays.asList("toto", "foobar")); 
CollectionUtils.merge(listA, listB, (s1, s2) -> s1.length() == s2.length(), String::length); 
System.out.println(listA); 
+0

Teşekkürler, önerinizi BiPredicate ile zaten uyguladım ve Arayüzü elimine ettim. 2. öneriye daha sonra da bakacağım - bir sargı sınıfını kullanarak eşitliği geçersiz kılacak ve belirli bir bağlamda hashCode'u kullanmak harika bir fikir. – SebastianRiemer

2

kullanabilirsiniz bir HashingStrategy tabanlı SetEclipse Collections

sizi MutableList arayüzünü kullanabilirsiniz: Eğer değiştiremiyorsanız

public static void merge(MutableList<AppleVariety> listA, MutableList<AppleVariety> listB) 
{ 
    MutableSet<AppleVariety> hashingStrategySet = HashingStrategySets.mutable.withAll(
     HashingStrategies.fromFunctions(AppleVariety::getManufacturerName, 
      AppleVariety::getVarietyName), 
     listA); 
    listA.addAllIterable(listB.asLazy().reject(hashingStrategySet::contains)); 
} 

List'dan listeA ve listB'nin türü:

public static void merge(List<AppleVariety> listA, List<AppleVariety> listB) 
{ 
    MutableSet<AppleVariety> hashingStrategySet = HashingStrategySets.mutable.withAll(
     HashingStrategies.fromFunctions(AppleVariety::getManufacturerName, 
      AppleVariety::getVarietyName), 
     listA); 
    listA.addAll(ListAdapter.adapt(listB).reject(hashingStrategySet::contains)); 
} 

Not: Eclipse koleksiyonlarına katkıda bulunuyorum.