【问题标题】:Apply Map Reduce in Java在 Java 中应用 Map Reduce
【发布时间】:2017-12-03 15:42:33
【问题描述】:

我是流媒体的初学者,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、taxOfParticularVehicle),例如:

(vehicleType,(noOfVehicle,noOfVehicle*taxOfParticularVehicle))

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

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

最后我想把它减少到以下集合:

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

这样 noOfVehicles 得到汇总,而 tax 是该组中车辆税的算术平均值。

【问题讨论】:

  • 是的,你是对的

标签: java mapreduce set tuples java-stream


【解决方案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&lt;String, Stats&gt;,其中包含从 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()));

并获得所需的映射。

【讨论】:

  • 是的,我可以这样做,但我想在这样的基础上对它们进行分组,以便收集特定的车辆类型编号以进行汇总
  • @Dhiresh Budhiraja 您能否在您的问题中添加规范或示例,以便我们了解您确切想要实现的目标?
  • 我已经给出了例子,你能帮我进一步
猜你喜欢
  • 2022-01-08
  • 2019-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-29
  • 2012-07-07
  • 1970-01-01
相关资源
最近更新 更多