ZetCode

Dart BytesBuilder

最后修改于 2025 年 4 月 4 日

Dart 中的 BytesBuilder 类提供了一种动态构建字节列表的高效方法。它对于网络协议、文件操作和二进制数据操作非常有用。

BytesBuilder 在内部管理一个可扩展的字节缓冲区,在构建字节序列时最大限度地减少内存分配。它是 Dart dart:typed_data 库的一部分。

基本定义

BytesBuilder 是一个用于构建字节列表的可变集合。它会根据需要自动增长,并提供高效添加字节的方法。

主要功能包括追加操作、长度跟踪和最终字节列表提取。它在构建大型字节序列时尤其高效。

BytesBuilder 的基本用法

此示例展示了使用 BytesBuilder 进行基本字节收集。

main.dart
import 'dart:typed_data';

void main() {
  var builder = BytesBuilder();
  
  builder.addByte(65); // 'A'
  builder.addByte(66); // 'B'
  builder.addByte(67); // 'C'
  
  var bytes = builder.toBytes();
  print(bytes); // [65, 66, 67]
}

我们创建一个 BytesBuilder,添加单个字节,然后提取最终的字节列表。当添加字节时,构建器会在内部处理内存管理。

$ dart main.dart
[65, 66, 67]

添加字节列表

此示例演示了一次性添加整个字节列表。

main.dart
import 'dart:typed_data';

void main() {
  var builder = BytesBuilder();
  
  builder.add([1, 2, 3]);
  builder.add([4, 5, 6]);
  
  var bytes = builder.toBytes();
  print(bytes); // [1, 2, 3, 4, 5, 6]
  
  print('Length: ${builder.length}'); // 6
}

我们添加了多个字节列表,它们会在内部进行连接。length 属性跟踪已添加的总字节数。这比逐个添加字节更有效。

$ dart main.dart
[1, 2, 3, 4, 5, 6]
Length: 6

构建二进制数据

此示例展示了使用 BytesBuilder 构建结构化二进制数据。

main.dart
import 'dart:typed_data';

void main() {
  var builder = BytesBuilder();
  
  // Add header
  builder.add([0xAA, 0xBB]);
  
  // Add payload length (2 bytes)
  builder.addByte(0);
  builder.addByte(5);
  
  // Add payload
  builder.add('Hello'.codeUnits);
  
  // Add checksum
  var bytes = builder.toBytes();
  var checksum = bytes.fold(0, (sum, byte) => sum + byte) & 0xFF;
  builder.addByte(checksum);
  
  print(builder.toBytes());
}

我们使用标头、长度、有效载荷和校验和构建了一个二进制消息。BytesBuilder 高效地处理了混合的数据类型和大小。

$ dart main.dart
[170, 187, 0, 5, 72, 101, 108, 108, 111, 142]

清除和重用

此示例演示了如何清除构建器以供重用。

main.dart
import 'dart:typed_data';

void main() {
  var builder = BytesBuilder();
  
  builder.add([1, 2, 3]);
  print('First build: ${builder.toBytes()}');
  
  builder.clear();
  builder.add([4, 5, 6]);
  print('Second build: ${builder.toBytes()}');
  
  print('Capacity: ${builder.length}'); // 3
}

clear() 方法在重用构建器时会重置构建器,同时保留内部容量。这可以避免在重新构建相似大小的字节序列时进行重新分配。

$ dart main.dart
First build: [1, 2, 3]
Second build: [4, 5, 6]
Capacity: 3

性能比较

此示例将 BytesBuilder 与手动列表连接进行了比较。

main.dart
import 'dart:typed_data';
import 'dart:math';

void main() {
  var random = Random();
  var data = List.generate(1000, (_) => random.nextInt(256));
  
  // Using BytesBuilder
  var stopwatch = Stopwatch()..start();
  var builder = BytesBuilder();
  for (var i = 0; i < 100; i++) {
    builder.add(data);
  }
  var builderTime = stopwatch.elapsedMicroseconds;
  
  // Using manual concatenation
  stopwatch.reset();
  var manual = <int>[];
  for (var i = 0; i < 100; i++) {
    manual = [...manual, ...data];
  }
  var manualTime = stopwatch.elapsedMicroseconds;
  
  print('BytesBuilder: $builderTime μs');
  print('Manual: $manualTime μs');
}

对于大型连接,BytesBuilder 的速度明显更快,因为它最大限度地减少了内存分配和复制。手动连接会创建许多中间列表。

$ dart main.dart
BytesBuilder: 1200 μs
Manual: 8500 μs

最佳实践

来源

Dart BytesBuilder 文档

本教程涵盖了 Dart 的 BytesBuilder 类,并提供了实用示例,展示了基本用法、性能优势以及字节操作的常见模式。

作者

我的名字是 Jan Bodnar,我是一名充满激情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出 所有 Dart 教程