Flink 系列第16篇:Flink 核心数据类型类详解(POJO、Row、Tuple)

在 Flink 编程中,POJO、Row、Tuple 是最常用的三大核心数据类型类,分别适用于不同的业务场景。本文将详细解析三者的定义、使用方法、优势劣势及适用场景,帮助开发者快速选择合适的数据类型。

一、POJO 类

1.1 POJO 是什么

POJO(Plain Old Java Object)中文常译为 “普通 Java 对象”,其核心是脱离框架束缚,专注于数据承载和基础行为,具体定义如下:

  • 不依赖特定框架:无需使用任何框架的注解(如 Spring、MyBatis 的注解);

  • 不继承特定父类、不实现特殊接口:仅遵循 Java 基础语法规范;

  • 核心思想:追求简单纯粹,让对象只关注数据存储和基础行为,避免被框架绑架。

典型 POJO 示例如下:

// 一个简单的POJO,代表用户信息
public class User {
    // 1. 私有字段(属性,用于存储数据)
    private String name;
    private int age;
    
    // 2. 公有默认构造方法(无参构造,必须存在)
    public User() {}
    
    // 3. 公有getter和setter方法(用于访问和修改私有字段)
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
    // 4. 可选的:重写toString(), equals(), hashCode() 等方法(便于调试和比较)
    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + "}";
    }
}

1.2 POJO 在 Flink 中的优势

在 Flink 中,POJO 是非常重要的数据类型,相比 Tuple 类型(难以管理)和 Row 类型(性能较低),其核心优势体现在 清晰的字段命名强大的类型安全 上。

Flink 会通过反射机制自动识别 POJO 的结构,由此带来两大核心好处:

  1. 字段名引用:在 DataStream API 或 Table API 中,可以直接使用字段名操作数据,代码可读性极高,无需记忆字段位置。// DataStream API 按键分组(直接引用字段名) dataStream.keyBy(user -> user.getName());
    // Table API 字段引用(直接使用字段名"name") tableEnv.fromDataStream(dataStream, $("name"));

  2. 类型安全:编译器会在编译时检查字段类型,避免运行时出现类型转换异常,降低调试成本。
    // 编译时已知getName()返回String类型,无需强制转换,安全可靠 String name = user.getName();

1.3 Flink 认可 POJO 的规则

要让 Flink 的反射机制正确识别类为 POJO,必须满足以下 4 个条件,缺一不可:

  1. 类必须是公有类(Public Class),不能是私有、保护或默认访问权限;

  2. 必须包含一个公有的无参构造方法(默认构造方法,若自定义了有参构造,需手动显式定义无参构造);

  3. 所有字段要么是公有的,要么有标准的 getter 和 setter 方法:

    • Getter 方法命名规范:getFieldName()(布尔类型字段可使用 isFieldName());

    • Setter 方法命名规范:setFieldName(value),参数类型与字段类型一致。

  4. 类中的字段类型必须是 Flink 支持的类型,主要包括:

    • 基本类型(如 int, long, double)及其包装类(Integer, Long, Double);

    • 其他符合 POJO 规则的自定义 POJO 类;

    • 集合类型(如 List, Map, Set);

    • 数组类型(如 String[], int[]);

    • 常用工具类(如 String, Date, BigDecimal)。

1.4 在 Flink 中使用 POJO

1.4.1 在 DataStream API 中使用

POJO 是 DataStream API 中最推荐使用的数据类型,尤其适合处理固定结构的数据,代码简洁易读。

// 1. 定义符合Flink规则的POJO类(SensorReading:传感器读数)
public class SensorReading {
    private String sensorId;
    private Long timestamp;
    private Double temperature;
    
    // 无参构造、getter、setter、toString方法(省略)
    
    // 有参构造(可选,便于快速创建对象)
    public SensorReading(String sensorId, Long timestamp, Double temperature) {
        this.sensorId = sensorId;
        this.timestamp = timestamp;
        this.temperature = temperature;
    }
}

// 2. 创建SensorReading类型的DataStream
DataStream<SensorReading> dataStream = env
    .fromElements(
        new SensorReading("sensor_1", 1677847200000L, 25.6),
        new SensorReading("sensor_2", 1677847200500L, 28.1)
    );

// 3. 使用字段名进行keyBy操作(清晰易懂)
dataStream.keyBy(SensorReading::getSensorId);

1.4.2 在 Table&SQL API 中使用

Flink 可自动识别 POJO 的字段名和类型,无需额外定义 Schema,可直接将 DataStream 转换为 Table 进行 SQL 操作。

// 1. 将POJO类型的DataStream转换为Table
Table table = tableEnv.fromDataStream(dataStream);

// 2. 在SQL中直接使用POJO的字段名进行查询
tableEnv.executeSql(
    "SELECT sensorId, AVG(temperature) " +
    "FROM " + table +
    " GROUP BY sensorId"
);

二、Row 类

2.1 Row 简介

Row 是 Flink 中表示一行数据的通用、动态类型数据结构,核心特点是灵活可扩展,与 Table API/SQL 深度绑定,具体特性如下:

  • 类似于关系型数据库中的一行记录,是 Table API 中数据的默认内部表示形式,SQL 查询结果的返回类型通常是 Row;

  • 可包含任意数量、任意类型的字段,支持嵌套结构(如 Row 中包含另一个 Row),适配动态 Schema 场景;

  • 字段访问方式:主要通过位置索引(从 0 开始)访问,Flink 1.14+ 支持通过字段名访问(需指定类型信息);

  • 常用于用户自定义函数(UDF)和表函数(UDTF)的输出,可通过 collect 方法输出多行数据;

  • 序列化优化:针对 Flink 的 TypeInformation 序列化框架进行了优化,效率高于普通 Java 对象,但低于 POJO 和 Tuple。

2.2 Row 类的构造与使用

2.2.1 创建 Row 类对象

Flink 提供了 3 种常用的 Row 创建方式,其中 Row.of() 是最推荐的方式,简洁高效。

// 创建Row的几种方式
Row row1 = new Row(3);          // 1. 指定字段数量(需后续手动设置字段值)
Row row2 = Row.of("Tom", 25);   // 2. 直接传入值(工厂方法,推荐),自动识别字段数量和类型
Row row3 = Row.withNames();     // 3. 创建带有字段名的Row(Flink 1.14+ 支持)

2.2.2 访问/设置字段

Row 的字段访问以索引为核心(从 0 开始),设置和获取字段时需注意类型匹配,避免运行时类型转换异常。

// 创建Row对象(包含3个字段:name、age、city)
Row row = Row.of("Alice", 30, "New York");

// 1. 获取字段值(需手动强制转换类型)
String name = (String) row.getField(0);      // "Alice" - 索引0(姓名)
int age = (int) row.getField(1);            // 30 - 索引1(年龄)
String city = (String) row.getField(2);     // "New York" - 索引2(城市)

// 2. 设置字段值(覆盖原有值,类型需与原有类型一致)
row.setField(0, "Bob");    // 将索引0的字段值改为"Bob"
row.setField(1, 35);       // 将索引1的字段值改为35

// 3. 获取字段总数
int arity = row.getArity();  // 返回3(当前Row有3个字段)

// 4. Row对象比较(需字段数量、类型、值完全一致)
Row rowA = Row.of("A", 1);
Row rowB = Row.of("A", 1);
boolean equal = rowA.equals(rowB);  // true

// 5. 转换为字符串(便于调试)
String str = rowA.toString();  // "A,1"

补充说明:Row 的字段类型由 getResultType 方法返回的 DataType 决定,需通过 DataTypes.createRowType 显式定义字段类型,且字段索引必须与定义顺序一致。

// 定义Row的字段类型:3个字段,依次为STRING、INT、BOOLEAN
DataType rowType = DataTypes.createRowType(
    DataTypes.STRING,  // 索引0:name
    DataTypes.INT,     // 索引1:age
    DataTypes.BOOLEAN  // 索引2:isStudent
);

2.2.3 与 Table API 交互

Row 是 Table API 的核心数据类型,可实现 DataStream 与 Table 的双向转换,适配 SQL 查询场景。

// 1. 创建Row类型的DataStream
DataStream<Row> dataStream = env.fromElements(
    Row.of("Alice", 25),
    Row.of("Bob", 30)
);

// 2. 将DataStream<Row>转换为Table(指定字段名)
Table table = tableEnv.fromDataStream(dataStream, $("name"), $("age"));

// 3. 执行SQL查询(返回结果仍为Row类型)
Table result = tableEnv.sqlQuery("SELECT name, age FROM table WHERE age > 26");
DataStream<Row> resultStream = tableEnv.toDataStream(result);

2.2.4 Row 在 FlinkSQL 中使用

在 Flink SQL 中,Row 常用于 UDTF(表函数)的输出,输出的 Row 会作为 SQL 表的一行数据。

-- 在Flink SQL中使用UDTF,输出Row类型数据
SELECT store_code, alias, id
FROM MyTable, 
LATERAL TABLE(AlienUDTF(json_str)) AS T(store_code, alias, id);

-- 输出结果(每一行对应一个Row对象):
-- store_code    | alias              | id
-- --------------+--------------------+------
-- "BJ_SF_001"   | "北京顺丰分拨中心" | 1001
-- "SH_SF_002"   | "上海顺丰分拨中心" | 1002

2.3 Row 的运行时类型信息

为确保 Flink 能正确序列化和处理 Row,需为其指定 RowTypeInfo(类型信息),明确字段的类型和名称(可选)。

2.3.1 定义基础类型信息

import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.typeutils.RowTypeInfo;

// 定义Row的字段类型(3个字段:name、age、isStudent)
RowTypeInfo typeInfo = new RowTypeInfo(
    Types.STRING,   // 第0个字段:String类型
    Types.INT,      // 第1个字段:INT类型
    Types.BOOLEAN   // 第2个字段:BOOLEAN类型
);

// 将类型信息应用到DataStream,确保序列化正确
DataStream<Row> dataStream = env
    .fromElements(Row.of("Alice", 25, true))
    .returns(typeInfo);

2.3.2 带字段名的类型信息

若需要通过字段名访问 Row 的字段,需在 RowTypeInfo 中指定字段名,实现“按名称访问”。

// 定义带字段名的RowTypeInfo
RowTypeInfo namedTypeInfo = new RowTypeInfo(
    new String[]{"name", "age", "isStudent"},  // 字段名数组
    new TypeInformation[]{Types.STRING, Types.INT, Types.BOOLEAN}  // 字段类型数组
);

// 按名称访问字段(需依赖namedTypeInfo)
Row row = Row.of("Alice", 25, true);
String name = (String) row.getField("name");  // 直接通过字段名获取,无需记索引

2.4 Row 的内存表示

Row 在内存中以“索引-值”的形式存储,字段顺序与定义的类型信息顺序一致,无需额外存储字段名(除非指定带字段名的类型信息),示例如下:

// 假设从HBase查询得到的结果,需封装为Row
Row row = new Row(3);
row.setField(0, "BJ_SF_001");    // 索引0:store_code(String)
row.setField(1, "北京顺丰分拨中心"); // 索引1:alias(String)
row.setField(2, 1001L);          // 索引2:id(Long)

// 内存中的Row结构(简化):
// +------------------+---------------------+-------+
// | 索引0            | 索引1               | 索引2 |
// +------------------+---------------------+-------+
// | "BJ_SF_001"      | "北京顺丰分拨中心"  | 1001L |
// +------------------+---------------------+-------+
//   类型: String        类型: String         类型: Long

2.5 Row vs POJO

Row 和 POJO 是 Flink 中最常用的两种数据类型,二者特性差异显著,适用场景不同,具体对比如下:

特性RowPOJO
类型安全❌ 运行时类型检查(需手动转换,易出错)✅ 编译时类型检查(安全可靠)
字段访问按位置或名称(动态,需记索引)通过 getter/setter(静态,字段名清晰)
性能⚠️ 较高序列化开销(低于 POJO)✅ 优化后的序列化(高效)
可读性❌ 低(需维护字段顺序,索引易混淆)✅ 高(字段名即业务含义,代码易读)
适用场景Table API 中间结果、动态结构数据DataStream API、固定结构数据
Schema 演化✅ 灵活(适应字段增减,无需重新编译)❌ 不灵活(字段增减需修改类,重新编译)

代码示例对比(直观体现可读性差异):

// 使用Row的方式(可读性差,需记索引)
Row row = Row.of("BJ_SF_001", "北京顺丰分拨中心", 1001L);
String storeCode = (String) row.getField(0); // 需记住索引0对应storeCode

// 使用POJO的方式(可读性好,字段名清晰)
public class StoreInfo {
    private String storeCode;
    private String alias;
    private Long id;
    // getters/setters(省略)
}
StoreInfo info = new StoreInfo("BJ_SF_001", "北京顺丰分拨中心", 1001L);
String storeCode = info.getStoreCode(); // 直接通过字段名获取,无需记索引

2.6 Row 使用总结

  • 优势:灵活适应动态 Schema,完美适配 Table API 的查询结果和 UDTF 输出,无需定义实体类;

  • 劣势:类型安全性低,需手动转换类型;性能略逊于 POJO;索引易出错,可读性差;

  • 最佳实践:

    • 优先在 Table API/SQL 结果处理、动态数据解析场景使用;

    • 显式定义 RowTypeInfo,减少运行时反射开销;

    • 优先使用 Row.of() 方法创建对象,比 new Row() + setField() 更高效。

三、Tuple(元组)类

3.1 Tuple 简介

Tuple 是 Flink 中用于存储多个不同类型元素的固定长度容器,核心特点是轻量、高效,但可读性较差,需注意:Tuple 不是集合,不能动态增减元素。

Flink 中的 Tuple 是泛型类,具体特性如下:

  • 长度固定:创建时即确定元素个数,不可变,从 Tuple0(空元组)到 Tuple25(最多25个元素)有具体实现类;

  • 泛型支持:通过泛型参数指定每个位置元素的类型,保证编译时类型安全;

  • 访问方式:通过公有字段 f0, f1, …, f24 访问元素(按位置访问);

  • 性能优异:底层实现简单,序列化/反序列化开销极低,是三种类型中性能最好的;

  • 常用场景:简单 Key-Value 对、临时数据处理、原型开发,最常用的是 Tuple2(二元组,用于表示 Key-Value 对)。

Tuple 特性汇总表:

性质描述示例
固定长度创建时确定元素个数,不可变Tuple2 永远有2个元素,无法新增或删除
类型安全通过泛型参数指定每个位置元素的类型Tuple2&lt;String, Integer&gt; 表示第一个元素是String,第二个是Integer
按位置访问通过公有字段 f0, f1… 访问元素myTuple.f0 获取第一个元素,myTuple.f1 获取第二个元素
效率高轻量级,序列化/反序列化开销低适合高性能、简单数据处理场景

3.2 关键提醒:Tuple 不是集合

很多开发者容易混淆 Tuple 和集合,需明确:Tuple 是“固定长度的容器”,每个字段(f0, f1)只能存储一个值,若需存储多个 Tuple,需借助集合(如 List)。

Tuple2 为例,其类定义简化如下:

public class Tuple2<K, V> extends Tuple {
    public K f0;  // 第一个元素(类型为K,只能存储一个值)
    public V f1;  // 第二个元素(类型为V,只能存储一个值)
}

正确使用示例(区分单个 Tuple 和多个 Tuple):

// 1. 单个二元组(存储一个Key-Value对)
Tuple2<String, Integer> singlePair = Tuple2.of("age", 25);
String key = singlePair.f0;   // "age"(第一个元素)
Integer value = singlePair.f1; // 25(第二个元素)

// 2. 多个二元组(需用集合存储,如List)
List<Tuple2<String, Integer>> pairs = Arrays.asList(
    Tuple2.of("age", 25),
    Tuple2.of("height", 180)
);

3.3 使用 Tuple

3.3.1 创建与赋值

Flink 提供 3 种 Tuple 创建方式,其中 TupleX.of()(X 为元素个数)是最简洁、推荐的方式。

import org.apache.flink.api.java.tuple.Tuple2; // 导入二元组
import org.apache.flink.api.java.tuple.Tuple3; // 导入三元组

// 方式1:直接创建并赋值(适用于已知元素的场景)
Tuple2<String, Integer> person = new Tuple2<>("Alice", 25);

// 方式2:使用静态工具方法.of()(更简洁,推荐)
Tuple3<String, Integer, Double> product = Tuple3.of("Laptop", 1, 999.99);

// 方式3:先创建,后设置字段(适用于元素需动态计算的场景)
Tuple2<Long, String> logEntry = new Tuple2<>();
logEntry.f0 = System.currentTimeMillis(); // 设置第一个字段(时间戳)
logEntry.f1 = "Error: File not found";    // 设置第二个字段(日志信息)

3.3.2 访问元素

Tuple 的元素通过公有字段 f0, f1, …, f24 直接访问,字段名与元素位置一一对应(第一个元素 f0,第二个 f1,以此类推)。

// 二元组访问
Tuple2<String, Integer> person = Tuple2.of("Alice", 25);
String name = person.f0;   // "Alice"(第一个元素)
Integer age = person.f1;   // 25(第二个元素)

// 三元组访问
Tuple3<String, Integer, Double> product = Tuple3.of("Laptop", 1, 999.99);
String itemName = product.f0;   // "Laptop"(第一个元素)
Integer quantity = product.f1;  // 1(第二个元素)
Double price = product.f2;      // 999.99(第三个元素)

3.3.3 在 DataStream 中使用 Tuple

Tuple 最常见的使用场景是 DataStream 中的 keyBy 操作,尤其适合简单的 Key-Value 对分组、聚合。

// 创建Tuple2类型的DataStream(第一个元素:品类,第二个元素:销量)
DataStream<Tuple2<String, Integer>> dataStream = env.fromElements(
    Tuple2.of("Category_A", 100),
    Tuple2.of("Category_B", 200),
    Tuple2.of("Category_A", 50)
);

// 1. 按Tuple的第一个字段(f0,品类名称)分组,对第二个字段(f1,销量)求和
dataStream.keyBy(value -> value.f0).sum(1).print(); 
// 输出结果:(Category_A, 150)、(Category_B, 200)

// 2. 按整个Tuple作为Key(很少用,仅当两个元素完全相同时才会被分到同一组)
dataStream.keyBy(0).sum(1).print(); 

// 3. 使用字段表达式访问(已逐渐被Lambda表达式取代)
dataStream.keyBy("f0").sum("f1").print();

3.4 Tuple 优缺点

优点

  1. 轻量高效:底层实现简单,无额外封装,序列化和网络传输开销小,性能是三种类型中最优的;

  2. 快速开发:无需定义 POJO 类,可直接使用 Flink 内置的 Tuple 类,适合快速原型开发和小型作业;

  3. 泛型安全:通过泛型参数指定每个位置的元素类型,编译时可检查类型错误,比 Row 类型更安全。

缺点

  1. 可读性差:代码中充斥 f0, f1, f2 等“魔法数字”,无法直观理解字段含义,随着字段增多,代码可维护性急剧下降;

  2. 重构困难:字段顺序是硬编码的,若需增加、删除或调整字段顺序,需修改所有访问该字段的代码(如将 f0 改为 f1);

  3. 长度限制:最多只能有 25 个字段(Tuple25),虽然大多数场景下足够,但无法满足字段数量较多的需求。

3.5 Tuple vs POJO vs Row 全面对比

三者作为 Flink 核心数据类型,各有优劣,适用场景差异明显,全面对比如下:

特性TuplePOJORow
可读性❌ 差(f0, f1 无含义)✅ 优(字段名清晰,贴合业务)⚠️ 中(可按名称访问,但需维护顺序)
类型安全✅ 优(编译时检查)✅ 优(编译时检查)❌ 差(运行时检查,需手动转换)
性能✅ 优(极高,无额外开销)✅ 优(高,序列化优化)⚠️ 中(有反射开销)
灵活性❌ 差(固定长度,不可扩展)✅ 优(可灵活定义字段,支持业务扩展)✅ 优(动态结构,适配字段增减)
开发效率✅ 优(无需定义类,直接使用)❌ 差(需定义类、getter/setter)✅ 优(无需定义类,动态创建)
适用场景简单KV对、临时数据、原型开发生产环境首选、固定结构数据、复杂业务Table API 中间结果、动态结构数据、UDTF输出

3.6 Tuple 使用建议

  • 避免使用:在新的 Flink 项目中,尽量避免使用 Tuple 作为主要的数据类型,尤其是字段数量超过 2 个的场景;

  • 临时场景:仅在快速测试、原型开发或处理简单的 Key-Value 对(如 Tuple2)时临时使用;

  • 生产推荐:生产环境中,始终优先使用 POJO,其清晰的字段名能大幅提升代码可读性和可维护性,这是大型、复杂项目成功的关键;

  • 特殊场景:某些 Flink 内置算子(如 Window 操作的部分方法)可能要求返回 Tuple 类型,这是少数不得不使用 Tuple 的场景。

总结:Tuple 是 Flink 中的“快捷方式”,虽然方便快捷,但为了代码的长远健康和可维护性,请养成使用 POJO 的好习惯。

四、整体总结

Flink 中 POJO、Row、Tuple 三大数据类型,核心定位和适用场景不同,开发者需根据业务需求选择:

  1. 生产环境首选 POJO:适合固定结构数据、复杂业务场景,兼顾可读性、类型安全和性能;

  2. Table API/SQL 场景首选 Row:适合动态结构数据、中间结果处理,灵活适配 SQL 查询;

  3. 临时/简单场景可使用 Tuple:适合简单 KV 对、原型开发,追求开发效率和高性能,但需注意可读性问题。

### Flink 支持的数据类型及其使用方法 Apache Flink 支持多种数据类型,这些数据类型可以分为基本数据类型、复杂数据类型和自定义数据类型Flink数据类型支持使得开发者能够灵活地处理不同型的数据源,并进行高效的转换和计算。 #### 1. 基本数据类型 Flink 支持 Java 和 Scala 中的所有基本数据类型,包括 `Integer`、`String`、`Double` 等。这些型可以直接用于 Flink 的数据流或批处理作业中[^1]。 ```java // 示例:使用基本数据类型 DataStream<Integer> integers = env.fromElements(1, 2, 3, 4, 5); ``` #### 2. 复杂数据类型 Flink 还支持复杂的内置数据结构,例如 `Tuple` 和 `Pojo` 型。`Tuple` 是一种轻量级的数据结构,支持最多 25 个字段,而 `Pojo` 型则允许开发者定义自己的来表示数据。 - **Tuple 型**: Tuple 是一种固定长度的不可变数据结构,适合用于简单的键值对场景。 ```java // 示例:使用 Tuple 数据类型 DataStream<Tuple2<String, Integer>> tuples = env.fromElements( new Tuple2<>("a", 1), new Tuple2<>("b", 2) ); ``` - **Pojo 型**: Pojo 型允许开发者定义自己的,只要该满足以下条件即可被识别为 Pojo: - 所有字段必须是公有的,或者提供公共 getter 和 setter 方法。 - 必须有一个无参构造函数。 ```java // 示例:使用 Pojo 数据类型 public class User { public String name; public int age; public User() {} public User(String name, int age) { this.name = name; this.age = age; } } DataStream<User> users = env.fromElements(new User("Alice", 25), new User("Bob", 30)); ``` #### 3. 自定义数据类型 对于更复杂的应用场景,开发者可以通过实现 `TypeInformation` 接口来自定义数据类型。这允许用户定义自己的序列化和反序列化逻辑[^3]。 ```java // 示例:自定义数据类型 public class CustomType implements Serializable { private String field1; private int field2; public CustomType() {} public CustomType(String field1, int field2) { this.field1 = field1; this.field2 = field2; } // Getters and Setters } DataStream<CustomType> customTypes = env.fromElements(new CustomType("data", 123)); ``` ### Flink 数据类型的练习示例 为了更好地理解 Flink数据类型及其使用方法,以下是一些练习示例: #### 示例 1:使用 Tuple 型进行简单聚合 ```java // 输入数据流 DataStream<Tuple2<String, Integer>> input = env.fromElements( new Tuple2<>("a", 1), new Tuple2<>("b", 2), new Tuple2<>("a", 3) ); // 聚合操作 DataStream<Tuple2<String, Integer>> result = input.keyBy(value -> value.f0) .sum(1); result.print(); ``` #### 示例 2:使用 Pojo 型进行实时数据分析 ```java // 定义 Pojo 型 public class Sale { public String product; public double amount; public Sale() {} public Sale(String product, double amount) { this.product = product; this.amount = amount; } } // 输入数据流 DataStream<Sale> sales = env.fromElements( new Sale("book", 10.5), new Sale("pen", 2.5), new Sale("book", 5.0) ); // 计算每种产品的总销售额 DataStream<Sale> totalSales = sales.keyBy(sale -> sale.product) .reduce((a, b) -> new Sale(a.product, a.amount + b.amount)); totalSales.print(); ``` #### 示例 3:使用自定义数据类型进行复杂数据处理 ```java // 自定义数据类型 public class Transaction { public String id; public double amount; public Transaction() {} public Transaction(String id, double amount) { this.id = id; this.amount = amount; } } // 输入数据流 DataStream<Transaction> transactions = env.fromElements( new Transaction("T1", 100.0), new Transaction("T2", 200.0) ); // 过滤金额大于 150 的交易 DataStream<Transaction> filteredTransactions = transactions.filter(transaction -> transaction.amount > 150); filteredTransactions.print(); ``` ### 总结 Flink 提供了丰富的数据类型支持,从基本数据类型到复杂数据类型,再到自定义数据类型,开发者可以根据具体需求选择合适的数据类型并进行高效的数据处理[^1]。此外,通过实际的练习示例,可以加深对 Flink 数据类型的理解和应用能力[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值