创建自定义的`Collector`

创建自定义的Collector

理解Collector是如何工作的

Collector只能在对象Stream.collect(),如果想要在数字stream中收集元素,你需要理解Collector接口。

简单的来说,Collector有4个基本组件构成。前2个组件用于收集stream流中的元素。第3个组件仅在并行stream流中需要。某些类型的collector需要第4个组件来对构建的容器做后置处理。

第一个组件用于提供收集stream流中元素所放置的容器,例如ArrayList, HashSet, HashMap等。

Creating such a container can be modeled with an instance of Supplier. This first component is called the supplier.

第二个组件用于添加stream流中元素到容器中。这个组件会被反复调用,知道stream流中的元素一个一个全部添加到容器中。

在Collector API中,这个组件是BiConsumer的实例。有2个参数:

  1. 第一个参数为容器(第一个组件所创建的)

  2. 第二个参数为stream流中的元素

This biconsumer is called the accumulator in the context of the Collector API.

这2个组件已经足以让Collector工作,但Stream API中有一个约束,使得需要更多的组件才能工作。

由于Stream API支持并行操作。 Collector API可以工作在这样的上下文:各个子stream流只能被自己Collector创建的容器所收集。

一旦所有的子stream流处理完成,就会产生数个容器(子stream流所创建)。这些容器完全相同,因为它们都是由一个组件所创建(第一个组件的Supplier)。现在你需要合并这些容器到一个容器中。因此Collector API需要第三个组件combiner,合并这些容器。

A combiner is modeled by an instance of BinaryOperator that takes two partially filled containers and returns one.

This BinaryOperator is also modeled by a BiConsumer in the collect() overloads of the Stream API.

第四个组件

The fourth component is called the finisher, and will be covered later in this part.

收集原始数据类型到Collection

特殊的数字stream流的collect(),只要前三个组件。

IntStream.collect()的三个参数:

  • Supplier实例,叫supplier

  • ObjIntConsumer实例,叫accumulator

  • BiConsumer实例,叫combiner

example

Supplier<List<Integer>> supplier                  = ArrayList::new;
ObjIntConsumer<List<Integer>> accumulator         = Collection::add;
BiConsumer<List<Integer>, List<Integer>> combiner = Collection::addAll;

List<Integer> collect =
    IntStream.range(0, 10)
             .collect(supplier, accumulator, combiner );

System.out.println("collect = " + collect);

result

collect = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

收集原始数据类型到StringBuffer

example

Supplier<StringBuffer> supplier                 = StringBuffer::new;
ObjIntConsumer<StringBuffer> accumulator        = StringBuffer::append;
BiConsumer<StringBuffer, StringBuffer> combiner = StringBuffer::append;

StringBuffer collect = 
    IntStream.range(0, 10)
             .collect(supplier, accumulator, combiner);

System.out.println("collect = " + collect);

result

collect = 0123456789

使用Collector的后置处理finisher

Collectors.collectintAndThen()

example1

Collection<String> strings =
    List.of("two", "three", "four", "five", "six", "seven", "eight", "nine",
            "ten", "eleven", "twelve");

Map<Integer, Long> histogram =
    strings.stream()
           .collect(
                   Collectors.groupingBy(
                           String::length,
                           Collectors.counting()));

Map.Entry<Integer, Long> maxValue =
    histogram.entrySet().stream()
             .max(Map.Entry.comparingByValue())
             .orElseThrow();

System.out.println("maxValue = " + maxValue);

example2

Collection<String> strings =
        List.of("two", "three", "four", "five", "six", "seven", "eight", "nine",
                "ten", "eleven", "twelve");

Function<Map<Integer, Long>, Map.Entry<Integer, Long>> finisher =
    map -> map.entrySet().stream()
              .max(Map.Entry.comparingByValue())
              .orElseThrow();

Map.Entry<Integer, Long> maxValue =
    strings.stream()
           .collect(
               Collectors.collectingAndThen(
                   Collectors.groupingBy(
                           String::length,
                           Collectors.counting()),
                   finisher
               ));

System.out.println("maxValue = " + maxValue);

使用Collectors.teeing()组合俩个Collector结果

jdk12新增Collectors.teeing()

假设你有下面记录

enum Color {
    RED, BLUE, WHITE, YELLOW
}

enum Engine {
    ELECTRIC, HYBRID, GAS
}

enum Drive {
    WD2, WD4
}

interface Vehicle {}

record Car(Color color, Engine engine, Drive drive, int passengers) implements Vehicle {}

record Truck(Engine engine, Drive drive, int weight) implements Vehicle {}

假设在车辆集合中找出所有电车和电卡车

List<Vehicle> electricVehicles = vehicles.stream()
    .collect(
        Collectors.teeing(
            Collectors.filtering(
                vehicle -> vehicle instanceof Car car && car.engine() == Engine.ELECTRIC,
                Collectors.toList()),
            Collectors.filtering(
                vehicle -> vehicle instanceof Truck truck && truck.engine() == Engine.ELECTRIC,
                Collectors.toList()),
            (cars, trucks) -> {
                cars.addAll(trucks);
                return cars;
            }));
Hive中的UDTF是用户定义的表生成函数,可以将一行输入转换为多行输出。以下是Hive中自定义UDTF函数的一些步骤: 1. 创建一个Java类,实现org.apache.hadoop.hive.ql.exec.UDTF接口。该接口需要实现initialize方法、process方法和close方法。 2. 在initialize方法中,可以获取函数的参数,例如输入列的名称和类型等。 3. 在process方法中,可以实现业务逻辑,并将结果输出到Collector对象中。 4. 在close方法中,可以进行一些清理工作。 5. 将Java类打包成jar文件,并上传到Hive中。 6. 在Hive中,使用ADD JAR命令加载jar文件。 7. 创建UDTF函数,使用CREATE FUNCTION命令,并指定INPUT格式和OUTPUT格式等参数。 8. 在Hive中使用UDTF函数,例如使用SELECT语句进行查询。 下面是一个简单的例子: ```java import org.apache.hadoop.hive.ql.exec.UDFArgumentException; import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException; import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException; import org.apache.hadoop.hive.ql.exec.UDFReturnType; import org.apache.hadoop.hive.ql.exec.description.JavaUDF; import org.apache.hadoop.hive.ql.metadata.HiveException; import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory; import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory; import java.util.ArrayList; @JavaUDF(description = "Split a string and output multiple rows", name = "explode_string", returnType = UDFReturnType.TABLE) public class ExplodeStringUDTF extends GenericUDTF { @Override public StructObjectInspector initialize(ObjectInspector[] args) throws UDFArgumentException { if (args.length != 1) { throw new UDFArgumentLengthException("explode_string takes only one argument"); } if (!args[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)) { throw new UDFArgumentTypeException(0, "explode_string takes only primitive types"); } ArrayList<String> fieldNames = new ArrayList<String>(); ArrayList<ObjectInspector> fieldOIs = new ArrayList<ObjectInspector>(); fieldNames.add("value"); fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs); } @Override public void process(Object[] args) throws HiveException { String input = args[0].toString(); String[] tokens = input.split(","); for (String token : tokens) { String[] fields = {token}; forward(fields); } } @Override public void close() throws HiveException { } } ``` 在Hive中,可以使用以下命令创建UDTF函数: ```sql CREATE FUNCTION explode_string AS 'com.example.ExplodeStringUDTF' USING JAR 'hdfs:///path/to/jar/file.jar'; ``` 使用以下命令查询数据: ```sql SELECT * FROM (SELECT 'a,b,c' AS input) s LATERAL VIEW explode_string(s.input) myTable AS myColumn; ``` 上述查询结果为: ``` myColumn ------- a b c ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值