2014-09-22 20 views
2

我有地圖格式在地圖對象計算平均多個號碼

Map<String, List<TableDTO>> 

public class TableDTO { 
    private String countryName; 
    private String sourceName; 
    private int year; 
    private Double usageValue; 
    private Double powerUsers; 

    //Setter & Getters 

} 

我想找到usageValues和超級用戶的平均水平,仍維持TableDTO結構和usageValue可以爲空,如果它完全忽略該對象。

<Chrome, <UK, Lorem, 2013, 2.90, 5.4>> 
<Chrome, <US, Lorem, 2013, 4.10, 1.5>> 
<Chrome, <EU, Lorem, 2013, 1.20, 0.22>> 
<Chrome, <Asia, Lorem, 2013, 3.90, -1.10>> 

<IE, <UK, Lorem, 2013, 1.40, 24.4>> 
<IE, <US, Lorem, 2013, 0.90, 14.4>> 
<IE, <EU, Lorem, 2013, 2.10, 0>> 
<IE, <Asia, Lorem, 2013, 0.90, 0.4>> 

<FF, <UK, Lorem, 2013, 0.10, 2.14>> 
<FF, <US, Lorem, 2013, 1.10, 4.0>> 
<FF, <EU, Lorem, 2013, , 4.4>> 
<FF, <Asia, Lorem, 2013, 2.90, 4.4>> 

結果預計

<1, <UK, Lorem, 2013, 1.47, 10.65>> 
<2, <US, Lorem, 2013, 2.03, 6.63>> 
<3, <Asia, Lorem, 2013, 2.57, 1.23>> 

現在的結果我更換了指數,這是很好的,現在的關鍵。你會注意到,由於對歐盟的FF值爲零,整個歐盟都被忽略了,但其餘的我的平均值被計算出來。

這又如何在Java中使用8 Lambda表達式來完成,或者我必須遍歷?

更新1: 這是據我得到了現在:

1. 
Map<String, List<TableDTO>> dump = mapOfAllData.values() 
       .stream() 
       .flatMap(list -> list.stream()) 
       .collect(Collectors.groupingBy(TableDTO::getCountryName)); 

Which give me a map with country names and the DTO orderd 

2. 
    dump.values().stream().flatMap(list -> list.stream()) 
       .filter((o -> !o.getUsageValue().isEmpty())) 
       .collect(Collectors.mapping(TableDTO::getUsageValue, Collectors.averagingDouble(Double::parseDouble))); 

基本上得到了平均水平,但不會刪除DTO其中usageValue是空的,這我在努力該解決的時刻。

更新2:

我設法從我的地圖中刪除不需要的國家。

我試圖找出如何找到兩個元素的平均,我有這樣的表達

newMap.values().stream().flatMap(list -> list.stream()) 
       .collect(Collectors.mapping(TableDTO::usageValue, Collectors.averagingDouble(s -> s.isEmpty() ? Double.NaN : Double.parseDouble(s)))); 
         // Collectors.mapping(TableDTO::powerUsers, Collectors.averagingDouble(c -> c.isEmpty() ? Double.NaN : Double.parseDouble(c)))); 

但我無法得到平均超級用戶。

+0

這與您之前的問題類似 - 如果您顯示了您的代碼以及哪些代碼不起作用,則會更容易。 – assylias 2014-09-22 12:52:30

+0

@assylias,說實話,我還沒有解決它,我的問題是,我沒有任何導師幫助我學習Java本身,所以我更依賴論壇和Javadocs – victor 2014-09-22 13:02:34

+1

@victor:特別是如果你想要學習,你應該嘗試先自己解決問題。在這裏問幾乎相同的問題表明沒有太多的學習成功,並表明你應該質疑你的學習方法。 – Holger 2014-09-22 13:36:38

回答

2

要了解,你想要一個平均在每個List<TableDTO>一個groupBycountryNamesourceNameyear但平均都在不同的領域呢?

我會預計usagePowerpowerUsersDouble,而不是您的代碼和您使用Double.parseDouble建議的字符串。

此代碼應該這樣做:

package stackoverflow; 

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.LinkedHashMap; 
import java.util.List; 
import java.util.Objects; 
import java.util.stream.Collectors; 

import javax.annotation.Nullable; 

public class TableDTO { 
    private final String countryName; 
    private final String sourceName; 
    private final int year; 
    @Nullable 
    private final Double usageValue; 
    private final Double powerUsers; 

    public TableDTO(final String countryName, final String sourceName, final int year, final Double usageValue, 
     final Double powerUsers) { 
    this.countryName = countryName; 
    this.sourceName = sourceName; 
    this.year = year; 
    this.usageValue = usageValue; 
    this.powerUsers = powerUsers; 
    } 

    public String getCountryName() {return countryName;} 
    public String getSourceName() {return sourceName;} 
    public int getYear() {return year;} 
    @Nullable public Double getUsageValue() {return usageValue;} 
    public Double getPowerUsers() {return powerUsers;} 

    @Override 
    public String toString() { 
    return "TableDTO [countryName=" + countryName + ", sourceName=" + sourceName + ", year=" + year + ", usageValue=" 
     + usageValue + ", powerUsers=" + powerUsers + "]"; 
    } 

    public static void main(final String[] args) { 

    final java.util.Map<String, java.util.List<TableDTO>> data = new LinkedHashMap<>(); 

    final List<TableDTO> chrome = new ArrayList<>(); 
    chrome.add(new TableDTO("UK", "Lorem", 2013, 2.90, 5.4)); 
    chrome.add(new TableDTO("US", "Lorem", 2013, 4.10, 1.5)); 
    chrome.add(new TableDTO("EU", "Lorem", 2013, 1.20, 0.22)); 
    chrome.add(new TableDTO("Asia", "Lorem", 2013, 3.90, -1.10)); 
    data.put("Chrome", chrome); 

    final List<TableDTO> ie = new ArrayList<>(); 
    ie.add(new TableDTO("UK", "Lorem", 2013, 1.40, 24.4)); 
    ie.add(new TableDTO("US", "Lorem", 2013, 0.90, 14.4)); 
    ie.add(new TableDTO("EU", "Lorem", 2013, 2.10, 0.)); 
    ie.add(new TableDTO("Asia", "Lorem", 2013, 0.90, 0.4)); 
    data.put("IE", ie); 

    final List<TableDTO> fx = new ArrayList<>(); 
    fx.add(new TableDTO("UK", "Lorem", 2013, 0.10, 2.14)); 
    fx.add(new TableDTO("US", "Lorem", 2013, 1.10, 4.0)); 
    fx.add(new TableDTO("EU", "Lorem", 2013, null, 4.4)); 
    fx.add(new TableDTO("Asia", "Lorem", 2013, 2.90, 4.4)); 
    data.put("FX", fx); 

    data.values() 
     .stream() 
     .flatMap(List::stream) 
     .collect(Collectors.groupingBy(dto -> Arrays.asList(dto.getCountryName(), dto.getSourceName(), dto.getYear()))) 
     .values() 
     .stream() 
     .filter(list -> list.stream().map(TableDTO::getUsageValue).noneMatch(Objects::isNull)) 
     .map(
      values -> { 
       final TableDTO root = values.iterator().next(); 

       final double usageValueAvg = values.stream().map(TableDTO::getUsageValue).filter(Objects::nonNull) 
        .collect(Collectors.averagingDouble(Double::doubleValue)); 
       final double powerUsersAvg = values.stream().map(TableDTO::getPowerUsers) 
        .collect(Collectors.averagingDouble(Double::doubleValue)); 

       return new TableDTO(root.getCountryName(), root.getSourceName(), root.getYear(), usageValueAvg, 
        powerUsersAvg); 

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

    } 
} 

結果是:

TableDTO [countryName=UK, sourceName=Lorem, year=2013, usageValue=1.4666666666666666, powerUsers=10.646666666666667] 
TableDTO [countryName=US, sourceName=Lorem, year=2013, usageValue=2.033333333333333, powerUsers=6.633333333333333] 
TableDTO [countryName=Asia, sourceName=Lorem, year=2013, usageValue=2.5666666666666664, powerUsers=1.2333333333333334] 

而且解釋:我已經採取了一些代碼來做到這一點。

  • 做一個flatMap超過data值:

    data.values() 
        .stream() 
        .flatMap(List::stream) 
    
  • 集團的TableDTO一些按鍵:我們不關心鍵,唯一重要的事情是,它正確地實現hashCodeequalsArrays.asList完成這項工作。否則,創建一個採用數組並使用Arrays.hashCode/equals的類Tuple。

    .collect(Collectors.groupingBy(dto -> Arrays.asList(dto.getCountryName(), dto.getSourceName(), dto.getYear()))) 
        .values() 
        .stream() 
    

    由於我們不想要列表,我們選擇值並使用流。

  • 我們篩選TableDTO其中包含一個空usageValue

    .filter(list -> list.stream().map(TableDTO::getUsageValue).noneMatch(Objects::isNull)) 
    
  • 然後我們做一個地圖,那你在哪裏找到一個解決辦法是失敗的:因爲該組的所有TableDTO共享相同的countryNamesourceNameyear的值。但不是usageValuepowerUsers

    因爲列表不能爲空,所以我們得到第一個元素。

    .map(
         values -> { 
          final TableDTO root = values.iterator().next(); 
    
  • 在其他結果,我們計算兩個平均值過濾任何空值usageValue

      final double usageValueAvg = values.stream().map(TableDTO::getUsageValue).filter(Objects::nonNull) 
           .collect(Collectors.averagingDouble(Double::doubleValue)); 
          final double powerUsersAvg = values.stream().map(TableDTO::getPowerUsers) 
           .collect(Collectors.averagingDouble(Double::doubleValue)); 
    
  • 然後我們返回基於三個分組鍵的新TableDTO和兩個平均值。

      return new TableDTO(root.getCountryName(), root.getSourceName(), root.getYear(), usageValueAvg, 
           powerUsersAvg); 
    
         }) 
    
  • 我們打印它,瞧! :)

     .forEach(System.out::println); 
    

我希望它能解決你的問題。

我在Eclipse中測試了它,它編譯了它,但由於編譯器與Lambdas的編譯器不同,它可能會因爲javac而失敗。

+0

之後才被計算出來,你知道我現在的位置出錯了。最終結果的數據結構是什麼? Map ,但我得到一個錯誤,並且必須將其定義爲Stream,這是否正確?我得到以下錯誤 - 「這是錯誤錯誤:(87,21)java:incompatible types:no instance s)類型變量(s)R存在,以便java.util.stream.Stream 符合java.util.Map >「 – victor 2014-09-23 14:52:43

+0

最後的結果將是一個'Stream ',你需要使用'collect(Collectors.toList())'來獲得一個'List '。如果你需要保存信息(比如」 Chrome「等),那麼我的例子是錯誤的:你需要一箇中間'map'操作來將每個'Map.Entry >'轉換成'List '(假設'String'是一個瀏覽器名稱;))。 – NoDataFound 2014-09-23 15:50:53

+0

謝謝,但這裏是我做的forEach(元素 - > _finalRank.add((TableDTO)元素));其中_finalRank是一個列表 victor 2014-09-24 08:27:50