2015-08-07 17 views
10

Tôi muốn thực hiện phương pháp sau:Làm cách nào để BigDecimals trung bình sử dụng Luồng?

public BigDecimal mean(List<BigDecimal> bigDecimals, RoundingMode roundingMode) { 
    BigDecimal sum = BigDecimal.ZERO; 
    int count=0; 
    for(BigDecimal bigDecimal : bigDecimals) { 
     if(null != bigDecimal) { 
      sum = sum.add(bigDecimal); 
      count++; 
     } 
    } 
    return sum.divide(new BigDecimal(count), roundingMode); 
} 

và cập nhật bằng cách sử dụng Luồng phát trực tiếp. Dưới đây là những gì tôi đã có, cho đến nay:

public BigDecimal average(List<BigDecimal> bigDecimals, RoundingMode roundingMode) { 
    BigDecimal sum = bigDecimals.stream() 
     .map(Objects::requireNonNull) 
     .reduce(BigDecimal.ZERO, BigDecimal::add); 
    long count = bigDecimals.stream().filter(Objects::nonNull).count(); 
    return sum.divide(new BigDecimal(count), roundingMode); 
} 

Có cách nào để thực hiện việc này mà không cần phát trực tuyến hai lần (lần thứ hai để tính)?

+1

các thể giảm chức năng ắc mất trong một tuple, một trong đó thể hiện tổng giá và người kia đếm? Havent nghiên cứu điều này xin lỗi – smk

Trả lời

11
BigDecimal[] totalWithCount 
       = bigDecimals.stream() 
       .filter(bd -> bd != null) 
       .map(bd -> new BigDecimal[]{bd, BigDecimal.ONE}) 
       .reduce((a, b) -> new BigDecimal[]{a[0].add(b[0]), a[1].add(BigDecimal.ONE)}) 
       .get(); 
BigDecimal mean = totalWithCount[0].divide(totalWithCount[1], roundingMode); 

mô tả văn bản bắt buộc của mã cho những người đang thấy là hữu ích (Bỏ qua nếu bạn tìm thấy mã đủ tự giải thích.):

  • Danh sách các BigDecimals được chuyển thành một dòng suối .
  • giá trị null được lọc ra khỏi luồng.
  • Luồng BigDecimals được ánh xạ thành luồng của hai phần tử của BigDecimal trong đó phần tử đầu tiên là phần tử từ luồng ban đầu và phần tử thứ nhất là phần giữ chỗ có giá trị một.
  • Trong việc giảm a giá trị (a,b) có tổng một phần trong phần tử đầu tiên và số phần trong phần tử thứ hai. Phần tử đầu tiên của phần tử b chứa mỗi giá trị BigDecimal để thêm vào tổng. Phần tử thứ hai của b không được sử dụng.
  • Giảm trả về tùy chọn sẽ trống nếu danh sách trống hoặc chỉ chứa giá trị null.
    • Nếu tùy chọn không trống, hàm Optional.get() sẽ trả về một mảng hai phần tử của BigDecimal trong đó tổng BigDecimals nằm trong phần tử đầu tiên và số lượng BigDecimals trong phần tử thứ hai.
    • Nếu tùy chọn trống, NoSuchElementException sẽ bị ném.
  • Giá trị trung bình được tính bằng cách chia tổng cho số.
5

Hoặc bạn có thể sử dụng thực hiện Collector này:

class BigDecimalAverageCollector implements Collector<BigDecimal, BigDecimalAccumulator, BigDecimal> { 

    @Override 
    public Supplier<BigDecimalAccumulator> supplier() { 
     return BigDecimalAccumulator::new; 
    } 

    @Override 
    public BiConsumer<BigDecimalAccumulator, BigDecimal> accumulator() { 
     return BigDecimalAccumulator::add; 
    } 

    @Override 
    public BinaryOperator<BigDecimalAccumulator> combiner() { 
     return BigDecimalAccumulator::combine; 
    } 

    @Override 
    public Function<BigDecimalAccumulator, BigDecimal> finisher() { 
     return BigDecimalAccumulator::getAverage; 
    } 

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

    @NoArgsConstructor 
    @AllArgsConstructor 
    static class BigDecimalAccumulator { 
     @Getter private BigDecimal sum = BigDecimal.ZERO; 
     @Getter private BigDecimal count = BigDecimal.ZERO; 

     BigDecimal getAverage() { 
      return BigDecimal.ZERO.compareTo(count) == 0 ? 
        BigDecimal.ZERO : 
        sum.divide(count, 2, BigDecimal.ROUND_HALF_UP); 
     } 

     BigDecimalAccumulator combine(BigDecimalAccumulator another) { 
      return new BigDecimalAccumulator(
        sum.add(another.getSum()), 
        count.add(another.getCount()) 
      ); 
     } 

     void add(BigDecimal successRate) { 
      count = count.add(BigDecimal.ONE); 
      sum = sum.add(successRate); 
     } 
    } 

} 

Và sử dụng nó như thế:

BigDecimal mean = bigDecimals.stream().collect(new BigDecimalAverageCollector()); 

Lưu ý: ví dụ sử dụng chú thích dự án Lombok để rút ngắn mã keo.

5

Bạn không cần truyền trực tiếp hai lần. Đơn giản chỉ cần gọi List.size() cho count:

public BigDecimal average(List<BigDecimal> bigDecimals, RoundingMode roundingMode) { 
    BigDecimal sum = bigDecimals.stream() 
     .map(Objects::requireNonNull) 
     .reduce(BigDecimal.ZERO, BigDecimal::add); 
    return sum.divide(new BigDecimal(bigDecimals.size()), roundingMode); 
} 
0
List<BigDecimal> bigDecimalList = Arrays.asList(
BigDecimal.TEN, 
BigDecimal.valueOf(2.23), 
BigDecimal.valueOf(12.32)); 


BigDecimal average = bigDecimalList.stream().reduce((x,y) -> x.add(y)).get() 
.divide(BigDecimal.valueOf(bigDecimalList.size()),2)); 


System.out.println("avarage = "+average); 
Các vấn đề liên quan