java.util.ObjectsクラスにcheckIndex()なるメソッドが増えてた話 #java9
序章
Java1.8とJava9のArrayListのソースを比較していたところ、set(int index, E element)/get(int)メソッドの中身が変わっていることに気づきました。
setメソッド
- Java1.8
public E set(int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
- Java9
public E set(int index, E element) { Objects.checkIndex(index, size); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
getメソッド
- Java1.8
public E get(int index) { rangeCheck(index); return elementData(index); }
- Java9
public E get(int index) { Objects.checkIndex(index, size); return elementData(index); }
Objectsクラスを見てみる
ObjectsクラスにcheckIndex()なるメソッドが追加されたようです。中身を見てみます。
/** * Checks if the {@code index} is within the bounds of the range from * {@code 0} (inclusive) to {@code length} (exclusive). * * <p>The {@code index} is defined to be out-of-bounds if any of the * following inequalities is true: * <ul> * <li>{@code index < 0}</li> * <li>{@code index >= length}</li> * <li>{@code length < 0}, which is implied from the former inequalities</li> * </ul> * * @param index the index * @param length the upper-bound (exclusive) of the range * @return {@code index} if it is within bounds of the range * @throws IndexOutOfBoundsException if the {@code index} is out-of-bounds * @since 9 */ @ForceInline public static int checkIndex(int index, int length) { return Preconditions.checkIndex(index, length, null); }
Preconditionsなる慣れないクラスが出てきました。
どうやら、Java9で新たに追加されたクラスの様で、パッケージは jdk.internal.util です。
なんとなく、一般開発者はあまり直で触らない方がよさそうな雰囲気のパッケージ名です。深入りせずに、戻ることにします。
Objectsクラスに戻ってみると、checkIndex()メソッド以外にcheckFromToIndex()や、checkFromIndexSize()というメソッドも追加されています。
/** * Checks if the sub-range from {@code fromIndex} (inclusive) to * {@code toIndex} (exclusive) is within the bounds of range from {@code 0} * (inclusive) to {@code length} (exclusive). * * <p>The sub-range is defined to be out-of-bounds if any of the following * inequalities is true: * <ul> * <li>{@code fromIndex < 0}</li> * <li>{@code fromIndex > toIndex}</li> * <li>{@code toIndex > length}</li> * <li>{@code length < 0}, which is implied from the former inequalities</li> * </ul> * * @param fromIndex the lower-bound (inclusive) of the sub-range * @param toIndex the upper-bound (exclusive) of the sub-range * @param length the upper-bound (exclusive) the range * @return {@code fromIndex} if the sub-range within bounds of the range * @throws IndexOutOfBoundsException if the sub-range is out-of-bounds * @since 9 */ public static int checkFromToIndex(int fromIndex, int toIndex, int length) { return Preconditions.checkFromToIndex(fromIndex, toIndex, length, null); } /** * Checks if the sub-range from {@code fromIndex} (inclusive) to * {@code fromIndex + size} (exclusive) is within the bounds of range from * {@code 0} (inclusive) to {@code length} (exclusive). * * <p>The sub-range is defined to be out-of-bounds if any of the following * inequalities is true: * <ul> * <li>{@code fromIndex < 0}</li> * <li>{@code size < 0}</li> * <li>{@code fromIndex + size > length}, taking into account integer overflow</li> * <li>{@code length < 0}, which is implied from the former inequalities</li> * </ul> * * @param fromIndex the lower-bound (inclusive) of the sub-interval * @param size the size of the sub-range * @param length the upper-bound (exclusive) of the range * @return {@code fromIndex} if the sub-range within bounds of the range * @throws IndexOutOfBoundsException if the sub-range is out-of-bounds * @since 9 */ public static int checkFromIndexSize(int fromIndex, int size, int length) { return Preconditions.checkFromIndexSize(fromIndex, size, length, null); }
新たなメソッドを使ってみる
とりあえず、使ってみることにします。
import java.util.Objects; public class CheckSize { public static void main(String... args) { checkIndex(); checkFromToIndex(); checkFromIndexSize(); } private static void checkIndex() { int a = Objects.checkIndex(2, 8); System.out.println("checkIndex:" + a); } private static void checkFromToIndex() { int a = Objects.checkFromToIndex(2, 8, 10); System.out.println("checkFromToIndex:" + a); } private static void checkFromIndexSize() { int a = Objects.checkFromIndexSize(2, 8, 10); System.out.println("checkFromIndexSize:" + a); } }
実行結果はこうなりました。
checkIndex:2 checkFromToIndex:2 checkFromIndexSize:2
JavaDocにも書かれているように、引数に問題が無ければ一番目の引数をそのまま返すようです。 では、不正な値を渡したらどうなるのでしょう?
private static void checkIndex() { int a = Objects.checkIndex(8, 2); System.out.println("checkIndex:" + a); }
こんなエラーを吐きました。
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 8 out-of-bounds for length 2 at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64) at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70) at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248) at java.base/java.util.Objects.checkIndex(Objects.java:372) at test.CheckSize.checkIndex(CheckSize.java:16) at test.CheckSize.main(CheckSize.java:9)
他のメソッドだと以下の通りです。
- checkFromToIndex()メソッドとそのエラー内容
private static void checkFromToIndex() { int a = Objects.checkFromToIndex(8, 1, 10); System.out.println("checkFromToIndex:" + a); }
Exception in thread "main" java.lang.IndexOutOfBoundsException: Range [8, 1) out-of-bounds for length 10 at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64) at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromToIndex(Preconditions.java:76) at java.base/jdk.internal.util.Preconditions.checkFromToIndex(Preconditions.java:295) at java.base/java.util.Objects.checkFromToIndex(Objects.java:398) at test.CheckSize.checkFromToIndex(CheckSize.java:22) at test.CheckSize.main(CheckSize.java:10)
- checkFromIndexSize()メソッドとそのエラー内容
private static void checkFromIndexSize() { int a = Objects.checkFromIndexSize(2, 1, 1); System.out.println("checkFromIndexSize:" + a); }
Exception in thread "main" java.lang.IndexOutOfBoundsException: Range [2, 2 + 1) out-of-bounds for length 1 at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64) at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckFromIndexSize(Preconditions.java:82) at java.base/jdk.internal.util.Preconditions.checkFromIndexSize(Preconditions.java:343) at java.base/java.util.Objects.checkFromIndexSize(Objects.java:424) at test.CheckSize.checkFromIndexSize(CheckSize.java:29) at test.CheckSize.main(CheckSize.java:11)
まとめ
通常のロジックで使うにはちょっとエラーメッセージが不親切、という印象です。自分で丁寧に判定ロジックを書いて、状況に合わせてわかりやすいメッセージを作った方がいいかもしれません。
そもそも、使い道がほとんどなさそうです。フレームワーク側で使うかも?くらいでしょうか。何かこんなパターンの時使えるのでは、という閃きがありましたら、教えてください。
10/5追記
Objects.checkIndexメソッドはArrayList.java以外にどこから呼ばれてるのかなとソースコードをgrep検索したところ、java.util.ImmutableCollections.javaとjava.util.AbstractList.java からも呼ばれていました。とはいえ、この二つは開発者は意識しないと思うクラスなので本当に「へー」程度ですね。
#Java9 #String 文字列結合のパフォーマンス向上について
2017年9月にリリースされたJava絡みの公式チュートリアルまとめ
これは何の記事?
いつかやってみるかもしれないチュートリアルをまとめておく。
一覧
JUnit 5 User Guide
- 2017/9/10リリース。
- PDFへのリンクあり
Java Shell User’s Guide
- 2017/9/21リリース
Java Platform, Standard Edition Java Shell User’s Guide - Contents
Project Jigsaw: Quick Start Guide
- 2017/9/21リリース
Project Jigsaw: Quick Start Guide
Java EE Tutorial
- 2017/9/21リリース。
半角英字の結合をしたらJava8より遅くなった件の調査【原因不明】 #Java9 #String
これの続編です。 結論から言うと謎が深まっただけです。 tadashi.hatenablog.com
LATIN1/UTF16 の判定が追加になっている、と書いてあるので、判定用の値を取得する coder()メソッドの結果を見てみることにしました。 d.hatena.ne.jp
ソースはこちらです。
※リフレクションを使っています。
https://github.com/suzukitadashi/jmhTest/blob/master/jmhTest/src/main/java/jmhTest/StringTest.java
package jmhTest; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class StringTest { private static String[] list = {"a","b","c","d","e","f","g","h","i", "j"}; public static void main(String... args) { // String[] val = {"", "a", "1", "あ"}; // for(String str : val) { // checkCoder(str); // } test1StringLine(); } private static void checkCoder(String str) { System.out.println("------"); Class<String> c = String.class; try { Method m = c.getDeclaredMethod("coder"); m.setAccessible(true); byte b = (byte)m.invoke(str); System.out.println(str + " coder(): "+b); Field f = c.getDeclaredField("coder"); f.setAccessible(true); byte b1 = f.getByte(str); System.out.println(str + " coder: "+b1); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } public static void test1StringLine() { int i = 0; String str0 = list[i]; checkCoder(str0); i++; String str1 = list[i]; checkCoder(str1); i++; String str2 = list[i]; checkCoder(str2); i++; String str3 = list[i]; checkCoder(str3); i++; String str4 = list[i]; checkCoder(str4); i++; String str5 = list[i]; checkCoder(str5); i++; String str6 = list[i]; checkCoder(str6); i++; String str7 = list[i]; checkCoder(str7); i++; String str8 = list[i]; checkCoder(str8); i++; String str9 = list[i]; checkCoder(str9); String str = str0 + str1 + str2 + str3 + str4 + str5 + str6 + str7 + str8 + str9; checkCoder(str); } }
遅くなる理由は、LATIN1で判定して違う、ということでUTF-16の処理になるために処理時間が遅延しているのでは、と推測しました。
さて、結果はというと…
------ a coder(): 0 a coder: 0 ------ b coder(): 0 b coder: 0 ------ c coder(): 0 c coder: 0 ------ d coder(): 0 d coder: 0 ------ e coder(): 0 e coder: 0 ------ f coder(): 0 f coder: 0 ------ g coder(): 0 g coder: 0 ------ h coder(): 0 h coder: 0 ------ i coder(): 0 i coder: 0 ------ j coder(): 0 j coder: 0 ------ abcdefghij coder(): 0 abcdefghij coder: 0
coderの値が0ならLATIN1、1ならUTF-16と判定されるロジックなのですが、LATIN1と判定されているようです。
うーん、なんで半角数字同様のパフォーマンスが出てくれないのだろう…?
正式版Java9で文字列の結合の速度を確認する #Java9 #String
これは何?
2017/9/21にJava9が正式にリリースされました。 Java9ではStringのあれこれが変わったと聞いていますが、そんなことはどうでもいいのです。
維持屋にとっては、バージョンアップ後のパフォーマンスがどうなるのか、が重要なのです!!(極論)
前提
これと同じプログラムを使ってテストしました。 qiita.com
@YujiSoftware さんのブログで、VMオプションについて記載されていたのでそれについても試しています。
githubのソースの場所はこちら。 github.com
jmhの起動方法などはこちらにまとめています。 https://github.com/suzukitadashi/jmhTest/blob/master/jmhTest/command.txt
テストした端末のスペックなどは以下の通りです。
- Windows10 64bit
- Intel® Core™ i7-2820QM CPU @ 2.30GHz
- メモリは8GM
結果
注釈
- スマホからだと見づらいので、パソコンにするか、横にして画面を広げるか、などをお勧めします。
- Scoreの数字が大きいほど、パフォーマンスがいいと言えます。
半角英字
VM version: JDK 1.8.0_144, VM 25.144-b01
Benchmark Mode Cnt Score Error Units StringLineHankaku.call1StringLine thrpt 10 141.099 ± 1.144 ops/s StringLineHankaku.call2StringConcat thrpt 10 35.888 ± 0.221 ops/s StringLineHankaku.call3StringBuffer thrpt 10 97.689 ± 0.812 ops/s StringLineHankaku.call4StringBuilder thrpt 10 98.916 ± 3.052 ops/s
VM version: JDK 9-ea, VM 9-ea+134(Early Access版)
Benchmark Mode Cnt Score Error Units StringLineHankaku.call1StringLine thrpt 10 90.187 ± 2.961 ops/s StringLineHankaku.call2StringConcat thrpt 10 46.822 ± 3.277 ops/s StringLineHankaku.call3StringBuffer thrpt 10 68.937 ± 2.796 ops/s StringLineHankaku.call4StringBuilder thrpt 10 78.742 ± 2.665 ops/s
VM version: JDK 9, VM 9+181
VM options: なし
Benchmark Mode Cnt Score Error Units StringLineHankaku.call1StringLine thrpt 10 92.795 ± 2.711 ops/s StringLineHankaku.call2StringConcat thrpt 10 27.106 ± 17.205 ops/s StringLineHankaku.call3StringBuffer thrpt 10 53.108 ± 21.248 ops/s StringLineHankaku.call4StringBuilder thrpt 10 62.540 ± 21.275 ops/s
VM options: -XX:-CompactStrings -DCompactStringEnabled=false
Benchmark Mode Cnt Score Error Units StringLineHankaku.call1StringLine thrpt 10 108.819 ± 4.212 ops/s StringLineHankaku.call2StringConcat thrpt 10 50.795 ± 0.952 ops/s StringLineHankaku.call3StringBuffer thrpt 10 56.127 ± 0.568 ops/s StringLineHankaku.call4StringBuilder thrpt 10 104.129 ± 1.707 ops/s
半角数字
VM version: JDK 1.8.0_144, VM 25.144-b01
Benchmark Mode Cnt Score Error Units StringLineNumber.call1StringLine thrpt 10 112.127 ± 34.207 ops/s StringLineNumber.call2StringConcat thrpt 10 71.693 ± 0.270 ops/s StringLineNumber.call3StringBuffer thrpt 10 103.061 ± 1.590 ops/s StringLineNumber.call4StringBuilder thrpt 10 88.149 ± 35.149 ops/s
結果全文
VM version: JDK 9-ea, VM 9-ea+134(Early Access版)
Benchmark Mode Cnt Score Error Units StringLineNumber.call1StringLine thrpt 10 245.689 ± 13.801 ops/s StringLineNumber.call2StringConcat thrpt 10 132.742 ± 9.051 ops/s StringLineNumber.call3StringBuffer thrpt 10 49.300 ± 1.349 ops/s StringLineNumber.call4StringBuilder thrpt 10 52.594 ± 2.545 ops/s
結果全文
VM version: JDK 9, VM 9+181
VM options: なし
Benchmark Mode Cnt Score Error Units StringLineNumber.call1StringLine thrpt 10 187.399 ± 111.698 ops/s StringLineNumber.call2StringConcat thrpt 10 103.961 ± 50.983 ops/s StringLineNumber.call3StringBuffer thrpt 10 33.543 ± 21.675 ops/s StringLineNumber.call4StringBuilder thrpt 10 51.688 ± 0.346 ops/s
結果全文
VM options: -XX:-CompactStrings -DCompactStringEnabled=false
Benchmark Mode Cnt Score Error Units StringLineNumber.call1StringLine thrpt 10 238.952 ± 12.086 ops/s StringLineNumber.call2StringConcat thrpt 10 107.838 ± 3.610 ops/s StringLineNumber.call3StringBuffer thrpt 10 101.667 ± 97.174 ops/s StringLineNumber.call4StringBuilder thrpt 10 177.984 ± 3.698 ops/s
結果全文
全角文字
VM version: JDK 1.8.0_144, VM 25.144-b01
Benchmark Mode Cnt Score Error Units StringLineZenkaku.call1StringLine thrpt 10 141.133 ± 1.504 ops/s StringLineZenkaku.call2StringConcat thrpt 10 36.212 ± 0.202 ops/s StringLineZenkaku.call3StringBuffer thrpt 10 97.025 ± 2.155 ops/s StringLineZenkaku.call4StringBuilder thrpt 10 99.454 ± 1.509 ops/s
VM version: JDK 9-ea, VM 9-ea+134(Early Access版)
Benchmark Mode Cnt Score Error Units StringLineZenkaku.call1StringLine thrpt 10 92.686 ± 0.299 ops/s StringLineZenkaku.call2StringConcat thrpt 10 49.554 ± 0.684 ops/s StringLineZenkaku.call3StringBuffer thrpt 10 56.950 ± 6.474 ops/s StringLineZenkaku.call4StringBuilder thrpt 10 69.917 ± 0.210 ops/s
VM version: JDK 9, VM 9+181
VM options: なし
Benchmark Mode Cnt Score Error Units StringLineZenkaku.call1StringLine thrpt 10 93.176 ± 1.204 ops/s StringLineZenkaku.call2StringConcat thrpt 10 48.488 ± 0.728 ops/s StringLineZenkaku.call3StringBuffer thrpt 10 55.984 ± 6.048 ops/s StringLineZenkaku.call4StringBuilder thrpt 10 63.517 ± 0.586 ops/s
結果全文
VM options: -XX:-CompactStrings -DCompactStringEnabled=false
Benchmark Mode Cnt Score Error Units StringLineZenkaku.call1StringLine thrpt 10 109.511 ± 1.972 ops/s StringLineZenkaku.call2StringConcat thrpt 10 50.061 ± 2.419 ops/s StringLineZenkaku.call3StringBuffer thrpt 10 55.549 ± 0.624 ops/s StringLineZenkaku.call4StringBuilder thrpt 10 104.124 ± 1.103 ops/s
結果全文
まとめ
数値でない場合の結合速度は爆速になるどころか、遅くなっている。
- @YujiSoftware さんのブログを読む限りでは、半角英字は速くなってもいいはずなのだが…テストプログラムの書き方の問題であろうか?
VMオプションを付けると、StringBuilderもStringと同等のパフォーマンスになることがある模様。
- Java9に挙げた結果、パフォーマンスが遅くなって困るときはVMオプションの付与を検討してもいいかも。[要検証]
お願い
- 違う結果が出たよ、というのがありましたらテスト方法含めてどこかにまとめたうえで教えてください。勉強しに行きます。
JasperReportsを使うときのライブラリについて
クラスメソッド様のブログにはいつもお世話になっております。
2013年に出たブログですが、今でもたまにたどり着くこのページ。 依存ライブラリの部分がmavenだと若干古くなっているようなのでメモとして残しておく。
JasperReports LibraryはPDFを出力する場合にitextを利用しますが、この時に依存しているバージョンはLGPLである2.1系の最新バージョンである2.1.7にJasperReportsで独自のパッチを当てた、2.1.7.js2です。このバージョンはMavenのcentral repositoryに存在しないため、Jaspersoftのリポジトリを追加しなければなりません。
最新版(6.4.1)だと itext-2.1.7.js6 がMavenのcentral repositoryに存在するようで、リポジトリの追加は不要。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>xxxx.yyy</groupId> <artifactId>test1</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>test1</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <javac.target>1.7</javac.target> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports --> <dependency> <groupId>net.sf.jasperreports</groupId> <artifactId>jasperreports</artifactId> <version>6.4.1</version> </dependency> </dependencies> </project>
このページを参考に依存関係を確認する。
確認結果は以下の通り。
[INFO] xxxx.xxxx:test1:jar:0.0.1-SNAPSHOT [INFO] +- junit:junit:jar:3.8.1:test [INFO] \- net.sf.jasperreports:jasperreports:jar:6.4.1:compile [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.3:compile [INFO] +- commons-collections:commons-collections:jar:3.2.2:compile [INFO] +- commons-digester:commons-digester:jar:2.1:compile [INFO] +- commons-logging:commons-logging:jar:1.1.1:compile [INFO] +- com.lowagie:itext:jar:2.1.7.js6:compile [INFO] | \- org.bouncycastle:bcprov-jdk15on:jar:1.52:compile [INFO] +- org.jfree:jcommon:jar:1.0.23:compile [INFO] +- org.jfree:jfreechart:jar:1.0.19:compile [INFO] +- org.eclipse.jdt.core.compiler:ecj:jar:4.3.1:compile [INFO] +- org.codehaus.castor:castor-xml:jar:1.3.3:compile [INFO] | +- org.codehaus.castor:castor-core:jar:1.3.3:compile [INFO] | +- commons-lang:commons-lang:jar:2.6:compile [INFO] | +- javax.inject:javax.inject:jar:1:compile [INFO] | +- stax:stax:jar:1.2.0:compile [INFO] | | \- stax:stax-api:jar:1.0.1:compile [INFO] | \- javax.xml.stream:stax-api:jar:1.0-2:compile [INFO] +- com.fasterxml.jackson.core:jackson-core:jar:2.1.4:compile [INFO] +- com.fasterxml.jackson.core:jackson-databind:jar:2.1.4:compile [INFO] +- com.fasterxml.jackson.core:jackson-annotations:jar:2.1.4:compile [INFO] +- org.apache.lucene:lucene-core:jar:4.5.1:compile [INFO] +- org.apache.lucene:lucene-analyzers-common:jar:4.5.1:compile [INFO] +- org.apache.lucene:lucene-queryparser:jar:4.5.1:compile [INFO] | +- org.apache.lucene:lucene-queries:jar:4.5.1:compile [INFO] | \- org.apache.lucene:lucene-sandbox:jar:4.5.1:compile [INFO] | \- jakarta-regexp:jakarta-regexp:jar:1.4:compile [INFO] +- org.olap4j:olap4j:jar:0.9.7.309-JS-3:compile [INFO] +- com.google.zxing:core:jar:3.2.1:compile [INFO] \- com.ibm.icu:icu4j:jar:57.1:compile [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 53.129 s [INFO] Finished at: 2017-08-18T10:41:44+09:00 [INFO] Final Memory: 14M/64M [INFO] ------------------------------------------------------------------------
以上。
SXSSFWorkbookの不具合 #apachePoi
知らなかったので再現プログラムを書いてみた。
import java.io.FileOutputStream; import java.io.IOException; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.streaming.SXSSFWorkbook; public class WriteTest { public static void main(String[] args) { String outputFilePath = "out.xlsx"; Workbook book = null; FileOutputStream fout = null; try { book = new SXSSFWorkbook(); Font font = book.createFont(); font.setFontName("MS ゴシック"); font.setFontHeightInPoints((short) 9); CellStyle style = book.createCellStyle(); style.setWrapText(true); Sheet sheet = book.createSheet(); Row row = sheet.createRow(0); Cell cell0 = row.createCell(0); cell0.setCellValue("aaa\r\nbbb"); cell0.setCellStyle(style); Cell cell1 = row.createCell(1); cell1.setCellValue("aaa\rbbb"); cell1.setCellStyle(style); Cell cell2 = row.createCell(2); cell2.setCellValue("aaa\nbbb"); cell2.setCellStyle(style); Cell cell3 = row.createCell(3); cell3.setCellValue("aaa\\nbbb"); cell3.setCellStyle(style); fout = new FileOutputStream(outputFilePath); book.write(fout); } catch (IOException e) { e.printStackTrace(); } finally { try { book.close(); } catch (IOException e) { e.printStackTrace(); } } } }
処理結果はこうなり、A1セルの改行が2回行われていることが確認出来る。
原因となるコードはpoi-ooxml-3.xx.jarの org.apache.poi.xssf.streaming.SheetDataWriter.javaの371行目から379行目の部分。 ※3.17-beta1でも直っていないことを確認した。
// Special characters case '\n': case '\r': if (counter > last) { _out.write(chars, last, counter - last); } _out.write("
"); last = counter + 1; break;
#xa;はxml文書で改行コードを表すらしい。
当面の対応は、\r\nを\r、あるいは\nに置換するしかなさそうだけど、本対応はapacheにバグレポートを書くことだろうなぁ。 githubだったらissueに書けばいいんだろうけど、apache poiはsubversionで活動されているので報告先がわからない…。
というか、SXSSFWorkbookってあまり使われていないのかな。