有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

mapreduce在Java中应用Map Reduce

我对streams,mapreducefilter非常熟悉

我从我的Cassandra表中得到一个行列表,其中包含三个字段vehicleTypenoOfVehiclestaxPerParticularVehicleType

我想准备一组这3种类型的三元组,以便添加任何特定类型的车辆数量,同时三元组还应包含特定车辆类型的税收算术平均值

我正在应用我的映射,例如:

session.execute(statement).all().stream()
            .map(row -> new ImmutablePair<>(row.getString("vehicleType"), new ImmutablePair<>(row.getInt("noOfVehicles"), row.getFloat("tollTaxOfParticularType") * row.getInt("noOfVehicles"))))
            .reduce(x->{

            });

我无法将reduce应用于以下集合:

Set<Triple<String,Integer,Double>> set = new HashSet<>();

我举了一个例子,说明我希望通过Map Reduce实现的目标:

我正在映射表中的三个字段(vehicleType、noOfVehicle、TaxOfParticularHicle),例如:

(vehicleType,(noOfVehicle,noOfVehicle*taxOfParticularVehicle))

假设映射给了我这样一个数组:

[("A",(12,48)),("A",(10,30)),("B",(3,30)),("B",(4,70))]

最后,我想把它简化为以下几组:

[("A",22,39),("B",7,50)]

这样一来,就不会对所有车辆进行汇总,而税是该组车辆税的算术平均值


共 (1) 个答案

  1. # 1 楼答案

    如果不进行多次流式处理或在外部维护可变状态,这有点棘手。这些方法最干净的替代方法似乎是编写一个自定义Collector

    我对PairTriple以及诸如此类的东西不太熟悉,所以为了说明起见,我使用了具体的类: Data是单个数据点的持有者,对应于您的三重数据

    static final class Data {
        final String type;
        final int noOfVehicles;
        final double totalTax;
        Data(String type, int noOfVehicles, double totalTax) {
            this.type = type;
            this.noOfVehicles = noOfVehicles;
            this.totalTax = totalTax;
        }
    }
    

    接下来,我们需要一个助手类来保存可变缩减期间的状态,我将其称为Stats

    static final class Stats {
        int noOfVehiclesSum;
        double totalTaxSum;
        int count;
    
        @Override
        public String toString() {
            return "Stats{" + "noOfVehiclesSum=" + noOfVehiclesSum +
                   ", averageTax=" + (totalTaxSum / count) + '}';
        }
    }
    

    让我们创建一个测试数据列表

    List<Data> l = Arrays.asList(new Data("A", 12, 48.0),
                                 new Data("A", 10, 30.0),
                                 new Data("B", 3 , 30.0),
                                 new Data("B", 4 , 70.0),
                                 new Data("B", 5 , 20.0));
    

    作为减少的最终结果,我想要的是一个Map<String, Stats>,它包含从vehicleType到该类型的Stats对象的映射(包含该类型的车辆计数和税收平均值的总和)

    在这个例子中:{A=Stats{noOfVehiclesSum=22, averageTax=39.0}, B=Stats{noOfVehiclesSum=12, averageTax=40.0}}

    我不知道还有什么比编写自己的定制Collector更好的解决方案,在本例中,它看起来有点像以下内容:

    static class StatsCollector implements Collector<Data, Stats, Stats> {
        @Override
        public Supplier<Stats> supplier() {
            return Stats::new;
        }
    
        @Override
        public BiConsumer<Stats, Data> accumulator() {
            return (stats, data) -> {
                stats.noOfVehiclesSum += data.noOfVehicles;
                stats.totalTaxSum += data.totalTax;
                stats.count += 1;
            };
        }
    
        @Override
        public BinaryOperator<Stats> combiner() {
            return (lft, rght) -> {
                lft.noOfVehiclesSum += rght.noOfVehiclesSum;
                lft.totalTaxSum += rght.totalTaxSum;
                lft.count += rght.count;
                return lft;
            };
        }
    
        @Override
        public Function<Stats, Stats> finisher() {
            return Function.identity();
        }
    
        @Override
        public Set<Characteristics> characteristics() {
            return EnumSet.of(Collector.Characteristics.IDENTITY_FINISH);
        }
    }
    

    最后,在完成所有这些工作后,你将能够写作

    Map<String, Stats> result = l.stream()
                                 .collect(Collectors.groupingBy(data -> data.type,
                                                                new StatsCollector()));
    

    并获得所需的映射