メモリ消費量を抑えてxlsxファイルを読み込みたい

Apache POIを利用してxlsxファイルを読み込む方法として、インターネット上ではUser APIがよく紹介されています (`HSSFWorkbook`とかを利用するもの)。これは便利ですが、読み込む対象のxlsxファイルをすべてメモリ上に展開するので、巨大なxlsxファイルを読み込む場合は、大量のメモリを消費する場合があります。

ではメモリ消費量を抑えて、xlsxファイルを読み込みたい場合はどうすればよいかというと、Event APIというAPIを利用します。自分はこのAPIをよく利用するのですが、インターネット上に日本語情報が少なく、実装のたびに困っているので、自分用にまとめておこうと思います。ちなみに以下の記事も参考にしてください。

https://qiita.com/neko_the_shadow/items/4ee737e9fb98dfb275b4

以下はEvent APIを利用し、対象のシートを読み込んで、2次元配列に変換するサンプルです。

```java
public class SheetToMatrix {

    public List<List<String>> toMatrix(Path path, String sheetName) {
        try (OPCPackage opcPackage = OPCPackage.open(path.toFile(), PackageAccess.READ)) {
            XSSFReader xssfReader = new XSSFReader(opcPackage);
            SheetIterator sheetsData = (SheetIterator) xssfReader.getSheetsData();

            while (sheetsData.hasNext()) {
                try (InputStream inputStream = sheetsData.next()) {
                    if (!Objects.equals(sheetsData.getSheetName(), sheetName)) {
                        continue;
                    }

                    XMLReader sheetParser = XMLHelper.newXMLReader();
                    SheetToMatrixHandler sheetToMatrixHandler = new SheetToMatrixHandler();
                    ContentHandler contentHandler = new XSSFSheetXMLHandler(
                            xssfReader.getStylesTable(),
                            new ReadOnlySharedStringsTable(opcPackage),
                            sheetToMatrixHandler,
                            false);
                    sheetParser.setContentHandler(contentHandler);
                    sheetParser.parse(new InputSource(inputStream));
                    return sheetToMatrixHandler.getMatrix();
                }
            }

            throw new RuntimeException("対象のシートがありません: " + sheetName);
        } catch (InvalidOperationException
                | IOException
                | OpenXML4JException
                | SAXException
                | ParserConfigurationException e) {
            throw new RuntimeException(e);
        }
    }

    public static class SheetToMatrixHandler implements SheetContentsHandler {

        private List<List<String>> matrix;

        public SheetToMatrixHandler() {
            matrix = new ArrayList<>();
        }

        public List<List<String>> getMatrix() {
            return matrix;
        }

        @Override
        public void cell(String cellReference, String formattedValue, XSSFComment comment) {
            CellReference cr = new CellReference(cellReference);
            int row = cr.getRow();
            int col = cr.getCol();

            while (matrix.size() <= row) {
                matrix.add(new ArrayList<>());
            }
            while (matrix.get(row).size() <= col) {
                matrix.get(row).add("");
            }

            matrix.get(row).set(col, formattedValue);
        }

        @Override
        public void startRow(int rowNum) {
            // PASS
        }

        @Override
        public void endRow(int rowNum) {
            // PASS
        }
    }
}
```

実際に利用してみましょう。たとえば以下のようなxlsxファイルがあるとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105859/2459e1a4-f7bc-9f3c-f3f2-54e9019d3989.png)

「商品」シートを読み込んで、2次元配列に変換し、内容をコンソールに出力してみましょう。

```java
List<List<String>> matrix = new SheetToMatrix().toMatrix(Paths.get("商品.xlsx"), "商品");
System.out.println(matrix);
```

以下のような結果がコンソールに出力されるはずです。

```
[[りんご, 100, 1], [みかん, 200], [いちご, , 3], [], [すいか, 400, 4], [], [], [ばなな, 500, 5]]
```

__環境情報__

```xml:pom.xml
<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <maven.compiler.source>17</maven.compiler.source>
  <maven.compiler.target>17</maven.compiler.target>
</properties>

<dependencies>
  <!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
  <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi</artifactId>
      <version>5.2.3</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
  <dependency>
      <groupId>org.apache.poi</groupId>
      <artifactId>poi-ooxml</artifactId>
      <version>5.2.3</version>
  </dependency>
  <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
  <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.19.0</version>
  </dependency>
</dependencies>
```