2017-12-14 105 views
6

Giả sử tôi có danh sách dưới đây tôi muốn trả về một kết quả mà chỉ có một người với tên "Sam"-"Fred" nhưng với 25 lượngLàm thế nào để hợp nhất một danh sách các đối tượng tương tự nhưng tổng hợp một số tính chất với Java 8

public class Java8Test{ 


    private static class Person { 
      private String name; 
      private String lastName; 
      private int amount; 

      public Person(String name, String lastName, int amount) { 
       this.name = name; 
       this.lastName = lastName; 
       this.amount = amount; 
      } 
     } 


     public static void main(String[] args) { 
      List<Person> people = new ArrayList<>(); 
      people.add(new Person("Sam","Fred",10)); 
      people.add(new Person("Sam","Fred",15)); 
      people.add(new Person("Jack","Eddie",10)); 
      // WHAT TO DO HERE ? 


     } 
    } 

LƯU Ý:

Ví dụ trên chỉ là để làm rõ, những gì tôi đang tìm kiếm là một bản đồ chung/giảm như chức năng với Java 8.

+1

bạn đã làm gì cố gắng? Bạn bị kẹt ở đâu? –

+0

@VitalijZadneprovskij // LÀM GÌ ĐÂY? Đây là nơi tôi bị kẹt: D – Adelin

Trả lời

0

Lớp học Person của bạn sẽ trông giống như sau:

private static class Person { 
    private String name; 
    private String lastName; 
    private int amount; 

    public Person(String name, String lastName, int amount) { 
     this.name = name; 
     this.lastName = lastName; 
     this.amount = amount; 
    } 

    public String getName() {return name;} 
    public String getLastName() {return lastName;} 
    public int getAmount() {return amount;} 
    public String toString() { 
     return name + "/" + lastName + "/" + String.valueOf(amount); 
    } 
} 

Như bạn có thể thấy tôi đã thêm getters công khai cho hồ sơ của bạn. Để đạt được những gì bạn muốn, hãy sử dụng đoạn mã sau:

Person person = people.stream() 
    .filter(p -> p.getName().equals("Sam")) 
    .filter(p -> p.getLastName().equals("Fred")) 
    .reduce(new Person("Sam","Fred",0), (p1, p2) -> { 
     p1.amount += p2.amount; 
    return new Person("Sam","Fred", p1.amount); 
}); 
people.add(person); 

Person p = people.stream() 
    .reduce((p1, p2) -> p1.amount > p2.amount ? p1 : p2).get(); 

Kết quả là một đối tượng người duy nhất với số lượng 25.

3

Có lẽ là một cách để sử dụng Stream, Collectors.groupingby và như thế, nhưng thay vào đó tôi muốn chỉ cần sử dụng một vòng lặp kiểu cũ với một số hữu ích Java 8 chức năng và lambdas:

Map<List<String>, Person> persons = new LinkedHashMap<>(); 
for (Person p : people) { 
    persons.compute(Arrays.asList(p.getName(), p.getLastName()), 
      (s, p2) -> p2 == null ? p : new Person(p.getName(), p.getLastName(), p.getAmount() + p2.getAmount())); 
} 

Kết quả là một Map, với giá trị là những người có "tổng hợp".

{[Sam, Fred]=Person(name=Sam, lastName=Fred, amount=25), 
[Jack, Eddie]=Person(name=Jack, lastName=Eddie, amount=10)} 
3

tôi có thể nghĩ loại này cách hacky để làm điều đó:

TreeSet<Person> res = people.stream() 
     .collect(Collector.of(
        () -> new TreeSet<>(Comparator.comparing(Person::getName) 
             .thenComparing(Person::getLastName)), 
        (set, elem) -> { 
         if (!set.contains(elem)) { 
          set.add(elem); 
         } else { 
          Person p = set.ceiling(elem); 
          p.setAmount(elem.getAmount() + p.getAmount()); 
         } 
        }, 
        (left, right) -> { 
         throw new IllegalArgumentException("not for parallel"); 
        })); 

Đó là không làm thay đổi định nghĩa của Person ở tất cả. Đó là số Set được trả lại (theo firstnamelastname), nhưng đó là những gì bạn muốn ở đây.

3

Bạn có thể sử dụng các dòng groupingBy vào nhóm theo nhiều cột:

Function<Person, List<Object>> key = p -> Arrays.asList(p.name, p.lastName); 
final Map<List<Object>, Integer> collect = people.stream() 
    .collect(Collectors.groupingBy(key, Collectors.summingInt(p -> p.amount))); 
System.out.println(collect); 

kết quả trong

{[Sam, Fred]=25, [Jack, Eddie]=10}

Từ đó, bạn có thể tạo ra các trường hợp người mới từ các giá trị bản đồ.

4

Bạn có thể sử dụng groupingBy, reducing và những người khác công cụ để: - nhóm Person bởi cùng tên và lastName - tổng giá trị của số tiền của họ - tạo một người với những attributs

people = people.stream().collect(
         Collectors.groupingBy(o -> Arrays.asList(o.name, o.lastName), 
           Collectors.summingInt(p->p.amount)) 
      .entrySet() 
      .stream() 
      .map(Person::apply).collect(Collectors.toList()); 

people.forEach(System.out::println); 

//Prints : 
Sam Fred 25 
Jack Eddie 10 

Và cả hai đều giống nhau (IDE của tôi đề xuất nó cho tôi, tôi cho rằng tôi không thực sự biết cách hoạt động, nếu ai đó biết: giải thích cho chúng tôi trong nhận xét)

.map(Person::apply) 
.map(e -> new Person(e.getKey().get(0), e.getKey().get(1), e.getValue()) 
+4

'Người :: áp dụng'? –

+3

'Collectors.summingInt (Person :: getAmount)' có khả năng hiệu quả hơn 'Collectors.reducing (0, Person :: getAmount, Integer :: sum)' (và thậm chí hơi ngắn hơn)… '.map (Person :: apply) 'yêu cầu một phương thức' static' 'apply' không xác định trong lớp' Person'. Bạn có thể có nghĩa là '.map (e -> new Person (e.getKey(). Get (0), e.getKey(). Get (1), e.getValue())' – Holger

+0

Chỉ muốn chỉ ra rằng, để có thể tham chiếu 'Person :: getAmount', trường số lượng sẽ có getter. Đó là lý do tại sao tôi sử dụng' p -> p.amount' thay vì, ví dụ ban đầu thiếu getter (s) vì bất kỳ lý do gì. –

5

Bạn có thể lặp lại danh sách people của bạn và sử dụng một bản đồ để kết hợp những người có cùng name - lastName cặp:

Map<String, Person> map = new HashMap<>(); 
people.forEach(p -> map.merge(
    p.getName() + " - " + p.getLastName(),     // name - lastName 
    new Person(p.getName(), p.getLastName, p.getAmount()), // copy the person 
    (o, n) -> o.setAmount(o.getAmount() + n.getAmount()))); // o=old, n=new 

Bây giờ map.values() là một giảmCollection<Person> theo yêu cầu của bạn.

Nếu bạn có khả năng để thêm một bản sao-constructor và một vài phương pháp để lớp Person:

public Person(Person another) { 
    this.name = another.name; 
    this.lastName = another.lastName; 
    this.amount = another.amount; 
} 

public String getFullName() { 
    return this.name + " - " + this.lastName; 
} 

public Person merge(Person another) { 
    this.amount += another.amount; 
} 

Sau đó, bạn có thể đơn giản hóa phiên bản đầu tiên của mã này, như sau:

Map<String, Person> map = new HashMap<>(); 
people.forEach(p -> map.merge(p.getFullName(), new Person(p), Person::merge)); 

Điều này sử dụng phương pháp Map.merge, rất hữu ích cho các trường hợp này.

0

Chỉ cần sử dụng Collectors.toMap

private static class Person { 
    String getName() { return name; } 

    String getLastName() { return lastName;} 

    int getAmount() { return amount; } 

    private String name; 
    private String lastName; 
    private int amount; 

    public Person(String name, String lastName, int amount) { 
     this.name = name; 
     this.lastName = lastName; 
     this.amount = amount; 
    } 
} 


public static void main(String[] args) { 
    List<Person> people = new ArrayList<>(); 
    people.add(new Person("Sam", "Fred", 10)); 
    people.add(new Person("Sam", "Fred", 15)); 
    people.add(new Person("Jack", "Eddie", 10)); 

    people.stream() 
      .collect(Collectors.toMap(
        person -> Arrays.asList(person.getName(), person.getLastName()), 
        Person::getAmount, 
        Integer::sum) 
      ) 
      .forEach((key, value) -> System.out.println(key + " " + value)); 
} 
Các vấn đề liên quan