2016-10-12 32 views
4

我有一個類如下。使用lambda表達式來總結成員變量?

public class Votes{ 
    String name; 
    int likes; 
    int dislikes; 

    //constructors, getters and setters  
} 

我有一個如下列表。

List<Votes> votesList; 

假設我用一些元素填充上面的列表。我想聲明一個在該列表中執行分組和求和操作的方法。

作爲一個例子,假設我在列表中爲該方法提供了以下元素作爲input

votesList.add(new Votes("A", 10, 5)); 
votesList.add(new Votes("B", 15, 10)); 
votesList.add(new Votes("A", 20, 15)); 
votesList.add(new Votes("B", 10, 25)); 
votesList.add(new Votes("C", 10, 20)); 
votesList.add(new Votes("C", 0, 15)); 

該方法應輸出List<Votes>具有下列元素。

("A", 30, 20), 
("B", 25, 35), 
("C", 10, 35) 

是否有一種簡單的方法可以在Java8中使用流,lambda表達式來實現這一點?我知道如何使用collectors來完成,如果我只有一個int成員。

有人可以請解釋我如何解決這種情況?

+2

你必須寫你自己的收藏家。 https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collector.html#of-java.util.function.Supplier-java.util.function.BiConsumer-java.util。 function.BinaryOperator-java.util.stream.Collector.Characteristics ...- –

+0

@MarkoTopolnik我非常感謝,如果你能闡述一個答案。 :)) –

+0

@MarkoTopolnik你能給我一個關於我的答案的評論。 :))有什麼方法可以改進? –

回答

2

最後,我認爲這是最簡單的方法。:))

Map<String, List<Votes>> grouped = voteCountList.stream().collect(Collectors.groupingBy(r->r.getName())); 

List<Votes> collectedList = new ArrayList<>(); 

grouped.forEach((groupName, votes) -> collectedList.add(new Votes(groupName, 
       votes.stream().collect(Collectors.summingInt(r->r.getLikes())), 
       votes.stream().collect(Collectors.summingInt(r->r.getDislikes()))))); 
+1

看起來很好。如果可行,請與它一起。 –

+0

@ToddDunlap thnx .. yh。有用。 :)) –

2

這應該讓你開始,這不正是你想要的,因爲它不會產生最終名單。

Map<String, Votes> collected = votesList.stream().collect(Collectors.groupingBy(Votes::getName, 
     collectingAndThen(reducing(
       (originalVotes, newVotes) -> new Votes(originalVotes.getName(), originalVotes.getLikes() + newVotes.getLikes(), originalVotes.getDislikes() + newVotes.getDislikes())) 
       , Optional::get))); 

collected.forEach((key, value) -> { 
    System.out.println(key + "," + value.getLikes() + "," + value.getDislikes()); 
}); 
+0

嗨,Thanx很多。如果有的話,可以查看我的答案並發表評論。 :)) –

3

一種方法是使用groupingBy,與reducing結合:

Collection<Optional<Votes>> res = 
    votesList.stream().collect(groupingBy(v -> v.name, reducing((v1, v2) -> new Votes(v1.name, v1.likes + v2.likes, v1.dislikes + v2.dislikes)))).values(); 

這會給你一個Collection<Optional<Votes>>,您可以通過組合reducing收集和使用裝訂功能Optional::get擺脫它collectingAndThen收藏家,但這將開始看起來很難閱讀。

所以另一種選擇是使用toMap,併合並兩票時,他們具有相同的名稱:

Collection<Votes> res = 
    votesList.stream().collect(toMap(v -> v.name, 
            v -> v, 
            (v1, v2) -> new Votes(v1.name, v1.likes + v2.likes, v1.dislikes + v2.dislikes))).values(); 

從那裏,你可以使用ArrayList拷貝構造函數(或者通過使用collectingAndThenm -> new ArrayList<>(m.values()),或通過將以前的表達式放在這個拷貝構造函數的參數列表中)。

+0

嗨,Thanx很多。如果有的話,可以查看我的答案並發表評論。 :)) –

2

下面是使用自定義收集器的解決方案。

測試代碼:

final List<Votes> votesList = new ArrayList<>(); 
votesList.add(new Votes("A", 10, 5)); 
votesList.add(new Votes("B", 15, 10)); 
votesList.add(new Votes("A", 20, 15)); 
votesList.add(new Votes("B", 10, 25)); 
votesList.add(new Votes("C", 10, 20)); 
votesList.add(new Votes("C", 0, 15)); 

final List<Votes> totals = votesList.stream().collect(new VoteCollector()); 
totals.forEach(votes -> { 
    System.out.println(votes.getName() + " " + votes.getLikes() + " " + votes.getDislikes()); 
}); 

VoteCollector類:

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.EnumSet; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
import java.util.Map.Entry; 
import java.util.Set; 
import java.util.function.BiConsumer; 
import java.util.function.BinaryOperator; 
import java.util.function.Function; 
import java.util.function.Supplier; 
import java.util.stream.Collector; 
import java.util.stream.Collectors; 

public class VoteCollector implements Collector<Votes, Map<String, Votes>, List<Votes>> { 

    @Override 
    public Supplier<Map<String, Votes>> supplier() { 
     return HashMap::new; 
    } 

    @Override 
    public BiConsumer<Map<String, Votes>, Votes> accumulator() { 
     return (map, votes) -> { 
      final Votes mapVotes = map.get(votes.getName()); 
      if (mapVotes == null) { 
       map.put(votes.getName(), new Votes(votes.getName(), votes.getLikes(), votes.getDislikes())); 
      } else { 
       mapVotes.setLikes(mapVotes.getLikes() + votes.getLikes()); 
       mapVotes.setDislikes(mapVotes.getDislikes() + votes.getDislikes()); 
      } 
     }; 
    } 

    @Override 
    public BinaryOperator<Map<String, Votes>> combiner() { 
     return (map1, map2) -> { 
      for (final Entry<String, Votes> map2Entry : map2.entrySet()) { 
       final Votes map1Votes = map1.get(map2Entry.getKey()); 
       if (map1Votes == null) { 
        map1.put(map2Entry.getKey(), map2Entry.getValue()); 
       } 
       else { 
        map1Votes.setLikes(map1Votes.getLikes() + map2Entry.getValue().getLikes()); 
        map1Votes.setDislikes(map1Votes.getDislikes() + map2Entry.getValue().getDislikes()); 
       } 
      } 

      return map1; 
     }; 
    } 

    @Override 
    public Function<Map<String, Votes>, List<Votes>> finisher() { 
     return map -> map.values().stream().collect(Collectors.toList()); 
    } 

    @Override 
    public Set<Characteristics> characteristics() { 
     return Collections.emptySet(); 
    } 
} 

輸出是

A 30 20 
B 25 35 
C 10 35 

Here是許多很好的來源之一,學習寫收藏家:

+1

您可以將整理器簡化爲'map - > new ArrayList <>(map.values())'。此外,您可以使用'Collector.of'來構建一個使用這四個函數的'Collector',而不需要創建一個實現'Collector'接口的新類。 – Holger

+0

@Todd嗨,Thanx很多。如果有的話,可以查看我的答案並發表評論。 :)) –