A fast minimal dependency implementation of Apache Parquet
371
stars
852
commits
Java
primary language
Sep 11, 2026
updated
A reader and writer for the Apache Parquet file format, optimized for minimal dependencies and great performance. Available as a Java library and a command-line tool.
Hardwood gives applications fast and efficient support for reading and writing Parquet, without pulling in Hadoop, Avro, or the wider parquet-java dependency tree. It is built to be:
parquet-java library supports, and a drop-in shim module eases migration from it, with documented divergences where Hardwood applies stricter semantics (e.g. SQL three-valued notEq)Besides the core library, Hardwood provides a ready-to-use CLI for inspecting and analysing Parquet files, including an interactive TUI for exploring a file's schema, row groups, pages, and data.
Latest version: 1.0.0.Final, 2026-06-25
Full documentation is available at hardwood.dev.
<dependency>
<groupId>dev.hardwood</groupId>
<artifactId>hardwood-core</artifactId>
<version>1.0.0.Final</version>
</dependency>
Here's how you read a file with the row-based API:
import dev.hardwood.InputFile;
import dev.hardwood.reader.ParquetFileReader;
import dev.hardwood.reader.RowReader;
try (ParquetFileReader fileReader = ParquetFileReader.open(InputFile.of(path));
RowReader rowReader = fileReader.rowReader()) {
while (rowReader.hasNext()) {
rowReader.next();
long id = rowReader.getLong("id");
String name = rowReader.getString("name");
LocalDate birthDate = rowReader.getDate("birth_date");
Instant createdAt = rowReader.getTimestamp("created_at");
}
}
And here's how you write a file:
import dev.hardwood.OutputFile;
import dev.hardwood.writer.ParquetFileWriter;
import dev.hardwood.writer.RowWriter;
try (ParquetFileWriter writer = ParquetFileWriter.create(OutputFile.of(path), schema)) {
RowWriter rows = writer.rowWriter();
for (Person person : people) {
rows.writeRow(row -> row
.setLong("id", person.id())
.setString("name", person.name())
.setDate("birth_date", person.birthDate()));
}
}
See the Getting Started guide for detailed setup instructions.
InputFile.of(Path)) may be arbitrarily large; each individual column chunk must be at most 2 GB of compressed data.InputFile.of(ByteBuffer)) and object-store sources are limited to 2 GB per file. Split larger datasets across multiple files and read them with Hardwood.openAll(...) or ParquetFileReader.openAll(...).OutputFile.of(Path); output to object storage is coming soon.| Document | Purpose |
|---|---|
| ARCHITECTURE.md | High-level architecture and module layout. |
| CONTRIBUTING.md | How to contribute: workflow, commit format, PR expectations. |
| ROADMAP.md | Implementation status, roadmap, and milestones. |
| NATIVE_BUILD.md | How the GraalVM native CLI build works. |
| PERFORMANCE.md | Benchmark results and how to run performance tests. |
| TESTING.md | Manual testing recipes (e.g. S3 via s3proxy). |
| RELEASING.md | Release process. |
See hardwood.dev for a list of articles, talks, and podcasts about Hardwood.
Contributions are welcome! See CONTRIBUTING.md for the full guide — how to find work, the issue-first workflow, commit message format, and PR expectations.
good first issue and help wanted.LLM-assisted contributions are welcome, but vibe coding — accepting AI-generated changes without understanding them — is not. The aspiration is a high-quality, maintainable, performant, safe codebase.
See ROADMAP.md for the detailed implementation status, roadmap, and milestones.
This project requires Java 25 or newer for building (to create the multi-release JAR with Java 22+ FFM support). The resulting JAR runs on Java 21+ (libdeflate support requires Java 22+).
Docker must be running for the build to succeed, as the test suite uses Testcontainers to spin up services (e.g. S3 integration tests). If Docker is not available, the build will fail during the test phase for these tests.
It comes with the Apache Maven wrapper, i.e. a Maven distribution will be downloaded automatically, if needed.
Run the following command to build this project:
./mvnw clean verify
On Windows, run the following command:
mvnw.cmd clean verify
Pass the -Dquick option to skip all non-essential plug-ins and create the output artifact as quickly as possible:
./mvnw clean verify -Dquick
Run the following command to format the source code and organize the imports as per the project's conventions:
./mvnw process-sources
The hardwood CLI compiles to a GraalVM native binary. Requires GraalVM (Java 25+) installed locally — install via SDKMAN:
sdk install java 25.0.2-graalce
Then build the cli module and its dependencies:
./mvnw -Dnative package -pl cli -am
The resulting binary is at cli/target/hardwood-cli.
See NATIVE_BUILD.md for the full build guide — obtaining a Linux binary, the Docker image, and how the native build works (codec handling, build arguments).
The documentation site can be previewed locally using Docker:
# Build the image (once, or after changing requirements.txt)
docker build -t hardwood-docs docs/
# Serve locally with hot reload — preview at http://127.0.0.1:8000
docker run --rm -p 8000:8000 -v "$(pwd):/repo" hardwood-docs
# Build static site (output in docs/site/)
docker run --rm -v "$(pwd):/repo" hardwood-docs build -f docs/mkdocs.yml
The serve command polls the mounted repository, so edits to docs/content,
docs/overrides, docs/hooks, and docs/mkdocs.yml rebuild the site and
refresh the open browser tab. Changes to docs/requirements.txt require a
rebuild of the image.
A Docker Compose set-up is provided for running Claude Code with all build dependencies (Java 25, Maven, gh) pre-installed.
GH_TOKEN=<your-token> docker compose run --rm claude
Set GH_TOKEN to a GitHub personal access token so that Claude Code can interact with issues and pull requests. The project directory is mounted into the container at /workspace, and Claude Code configuration is persisted in a named volume across sessions.
See RELEASING.md.
To generate an API change report across all published modules (hardwood-core, hardwood-avro, hardwood-s3, hardwood-aws-auth):
tools/api-report.sh <PREVIOUS_VERSION> # HEAD (snapshot) vs <PREVIOUS_VERSION>
tools/api-report.sh <PREVIOUS_VERSION> <LATER_VERSION> # compare two published versions
In the default (single-argument) form the script installs the snapshot jars and compares HEAD against <PREVIOUS_VERSION>. With both arguments the build step is skipped and both sides are resolved from the Maven repository — useful to diff arbitrary released pairs (e.g. Beta2 against CR1).
The script writes a unified target/japicmp/api-report.diff (text) and target/japicmp/api-report.html (one document with a TOC linking to per-module sections). Per-module reports also land under target/japicmp/<artifactId>/ (HTML / Markdown / XML / diff). Internal packages (dev.hardwood.internal) are excluded. This is run automatically during releases.
See PERFORMANCE.md for benchmark results and instructions on running performance tests.
This code base is available under the Apache License, version 2.
(top 30 of 39)
Java
94.7%
Python
4.4%
A fast minimal dependency implementation of Apache Parquet
371
stars
852
commits
Java
primary language
Sep 11, 2026
updated
A reader and writer for the Apache Parquet file format, optimized for minimal dependencies and great performance. Available as a Java library and a command-line tool.
Hardwood gives applications fast and efficient support for reading and writing Parquet, without pulling in Hadoop, Avro, or the wider parquet-java dependency tree. It is built to be:
parquet-java library supports, and a drop-in shim module eases migration from it, with documented divergences where Hardwood applies stricter semantics (e.g. SQL three-valued notEq)Besides the core library, Hardwood provides a ready-to-use CLI for inspecting and analysing Parquet files, including an interactive TUI for exploring a file's schema, row groups, pages, and data.
Latest version: 1.0.0.Final, 2026-06-25
Full documentation is available at hardwood.dev.
<dependency>
<groupId>dev.hardwood</groupId>
<artifactId>hardwood-core</artifactId>
<version>1.0.0.Final</version>
</dependency>
Here's how you read a file with the row-based API:
import dev.hardwood.InputFile;
import dev.hardwood.reader.ParquetFileReader;
import dev.hardwood.reader.RowReader;
try (ParquetFileReader fileReader = ParquetFileReader.open(InputFile.of(path));
RowReader rowReader = fileReader.rowReader()) {
while (rowReader.hasNext()) {
rowReader.next();
long id = rowReader.getLong("id");
String name = rowReader.getString("name");
LocalDate birthDate = rowReader.getDate("birth_date");
Instant createdAt = rowReader.getTimestamp("created_at");
}
}
And here's how you write a file:
import dev.hardwood.OutputFile;
import dev.hardwood.writer.ParquetFileWriter;
import dev.hardwood.writer.RowWriter;
try (ParquetFileWriter writer = ParquetFileWriter.create(OutputFile.of(path), schema)) {
RowWriter rows = writer.rowWriter();
for (Person person : people) {
rows.writeRow(row -> row
.setLong("id", person.id())
.setString("name", person.name())
.setDate("birth_date", person.birthDate()));
}
}
See the Getting Started guide for detailed setup instructions.
InputFile.of(Path)) may be arbitrarily large; each individual column chunk must be at most 2 GB of compressed data.InputFile.of(ByteBuffer)) and object-store sources are limited to 2 GB per file. Split larger datasets across multiple files and read them with Hardwood.openAll(...) or ParquetFileReader.openAll(...).OutputFile.of(Path); output to object storage is coming soon.| Document | Purpose |
|---|---|
| ARCHITECTURE.md | High-level architecture and module layout. |
| CONTRIBUTING.md | How to contribute: workflow, commit format, PR expectations. |
| ROADMAP.md | Implementation status, roadmap, and milestones. |
| NATIVE_BUILD.md | How the GraalVM native CLI build works. |
| PERFORMANCE.md | Benchmark results and how to run performance tests. |
| TESTING.md | Manual testing recipes (e.g. S3 via s3proxy). |
| RELEASING.md | Release process. |
See hardwood.dev for a list of articles, talks, and podcasts about Hardwood.
Contributions are welcome! See CONTRIBUTING.md for the full guide — how to find work, the issue-first workflow, commit message format, and PR expectations.
good first issue and help wanted.LLM-assisted contributions are welcome, but vibe coding — accepting AI-generated changes without understanding them — is not. The aspiration is a high-quality, maintainable, performant, safe codebase.
See ROADMAP.md for the detailed implementation status, roadmap, and milestones.
This project requires Java 25 or newer for building (to create the multi-release JAR with Java 22+ FFM support). The resulting JAR runs on Java 21+ (libdeflate support requires Java 22+).
Docker must be running for the build to succeed, as the test suite uses Testcontainers to spin up services (e.g. S3 integration tests). If Docker is not available, the build will fail during the test phase for these tests.
It comes with the Apache Maven wrapper, i.e. a Maven distribution will be downloaded automatically, if needed.
Run the following command to build this project:
./mvnw clean verify
On Windows, run the following command:
mvnw.cmd clean verify
Pass the -Dquick option to skip all non-essential plug-ins and create the output artifact as quickly as possible:
./mvnw clean verify -Dquick
Run the following command to format the source code and organize the imports as per the project's conventions:
./mvnw process-sources
The hardwood CLI compiles to a GraalVM native binary. Requires GraalVM (Java 25+) installed locally — install via SDKMAN:
sdk install java 25.0.2-graalce
Then build the cli module and its dependencies:
./mvnw -Dnative package -pl cli -am
The resulting binary is at cli/target/hardwood-cli.
See NATIVE_BUILD.md for the full build guide — obtaining a Linux binary, the Docker image, and how the native build works (codec handling, build arguments).
The documentation site can be previewed locally using Docker:
# Build the image (once, or after changing requirements.txt)
docker build -t hardwood-docs docs/
# Serve locally with hot reload — preview at http://127.0.0.1:8000
docker run --rm -p 8000:8000 -v "$(pwd):/repo" hardwood-docs
# Build static site (output in docs/site/)
docker run --rm -v "$(pwd):/repo" hardwood-docs build -f docs/mkdocs.yml
The serve command polls the mounted repository, so edits to docs/content,
docs/overrides, docs/hooks, and docs/mkdocs.yml rebuild the site and
refresh the open browser tab. Changes to docs/requirements.txt require a
rebuild of the image.
A Docker Compose set-up is provided for running Claude Code with all build dependencies (Java 25, Maven, gh) pre-installed.
GH_TOKEN=<your-token> docker compose run --rm claude
Set GH_TOKEN to a GitHub personal access token so that Claude Code can interact with issues and pull requests. The project directory is mounted into the container at /workspace, and Claude Code configuration is persisted in a named volume across sessions.
See RELEASING.md.
To generate an API change report across all published modules (hardwood-core, hardwood-avro, hardwood-s3, hardwood-aws-auth):
tools/api-report.sh <PREVIOUS_VERSION> # HEAD (snapshot) vs <PREVIOUS_VERSION>
tools/api-report.sh <PREVIOUS_VERSION> <LATER_VERSION> # compare two published versions
In the default (single-argument) form the script installs the snapshot jars and compares HEAD against <PREVIOUS_VERSION>. With both arguments the build step is skipped and both sides are resolved from the Maven repository — useful to diff arbitrary released pairs (e.g. Beta2 against CR1).
The script writes a unified target/japicmp/api-report.diff (text) and target/japicmp/api-report.html (one document with a TOC linking to per-module sections). Per-module reports also land under target/japicmp/<artifactId>/ (HTML / Markdown / XML / diff). Internal packages (dev.hardwood.internal) are excluded. This is run automatically during releases.
See PERFORMANCE.md for benchmark results and instructions on running performance tests.
This code base is available under the Apache License, version 2.
(top 30 of 39)
Java
94.7%
Python
4.4%