2016-03-24 18 views
10

Tôi cần hợp nhất tất cả các thành phần của danh sách B vào danh sách khác listA.java 8 hợp nhất tất cả các phần tử của ListB vào ListA nếu không có mặt

Nếu một phần tử đã có mặt (dựa trên kiểm tra bình đẳng tùy chỉnh) trong listA Tôi không muốn thêm nó.

Tôi không muốn sử dụng Đặt và tôi không muốn ghi đè bằng() và hashCode().

Lý do là, tôi không muốn ngăn trùng lặp trong listA cho mỗi lần, tôi chỉ muốn không hợp nhất từ ​​listB nếu đã có các phần tử trong listA mà tôi xem là bằng nhau.

Tôi không muốn ghi đè bằng() và hashCode() vì điều đó có nghĩa là tôi cần đảm bảo, việc triển khai bằng() cho các phần tử được giữ trong mọi trường hợp. Tuy nhiên, có thể các phần tử từ listB không được khởi tạo đầy đủ, tức là chúng có thể bỏ sót một id đối tượng, trong đó các phần tử có thể có trong các phần tử của listA.

cách tiếp cận hiện tại của tôi liên quan đến một giao diện và một Utility-Function:

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

Và sau đó tôi muốn sử dụng nó như thế này:

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

để có được danh sách mới của tôi trong Lista với tất cả các yếu tố được hợp nhất từ ​​B.

Đây có phải là cách tiếp cận tốt không? Có cách nào tốt hơn/dễ hơn để thực hiện tương tự không?

Trả lời

7

Bạn muốn một cái gì đó như thế này:

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

Bạn không cần một giao diện HasEqualityFunction. Bạn có thể sử dụng lại BiPredicate để kiểm tra xem hai đối tượng có cân bằng với logic của bạn không.

Mã này chỉ lọc các phần tử trong listB không được chứa trong listA theo vị từ đã cho. Nó di chuyển ngang qua listA nhiều lần vì có các phần tử trong listB.


An thực hiện thay thế và performant tốt hơn là nên sử dụng một lớp wrapper mà kết thúc tốt đẹp các yếu tố của bạn và có như equals phương pháp ngữ của bạn:

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

này đầu tiên kết thúc tốt đẹp mọi phần tử bên trong một đối tượng Wrapper và thu thập chúng vào một số Set. Sau đó, nó lọc các phần tử của listB không được chứa trong bộ này. Kiểm tra bình đẳng được thực hiện bằng cách ủy thác cho biến vị ngữ đã cho. Ràng buộc là chúng tôi cũng cần phải cung cấp một số hashFunction để triển khai đúng hashCode.

Mẫu mã sẽ là:

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

Cảm ơn, tôi đã áp dụng đề xuất của bạn với BiPredicate và loại bỏ Giao diện. Tôi cũng sẽ xem xét đề xuất thứ hai của bạn sau - sử dụng lớp trình bao bọc để có thể ghi đè bằng và hashCode trong một ngữ cảnh cụ thể là một ý tưởng tuyệt vời. – SebastianRiemer

2

Bạn có thể sử dụng một HashingStrategy dựa Set từ Eclipse Collections

Nếu bạn có thể sử dụng giao diện MutableList:

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

Nếu bạn không thể thay đổi loại danh sáchA và danh sáchB từ List:

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

Lưu ý: Tôi là cộng tác viên cho Bộ sưu tập Eclipse.

Các vấn đề liên quan