Asakusa FrameworkのOperator DSLで使うStringOptionクラスについて。
|
StringOptionは、内部で(java.lang.Stringでなく)org.hadoop.io.Textを保持している。[2013-12-15]
したがって、getメソッドでは(Stringでなく)Textが返る。
Stringが欲しい場合はgetAsStringメソッドを使う。
モデルクラスでも、DMDL上でTEXTが指定されたプロパティーには、AsStringの付いたgetter/setterメソッドが生成される。
ただし、単純に値を移送するだけなら、AsStringの付いたメソッドを使うのは実行効率が悪い。
内部ではTextからString(new String(bytes, "UTF-8")
相当)、あるいはStringからTextへの変換(string.getBytes("UTF-8")
相当)が発生しているから。
//実行効率が悪い private void example(SalesDetail model1, SalesDetail model2) { String code = model2.getStoreCodeAsString(); model1.setStoreCodeAsString(code); }
↓↑実行効率以外は同じ
import import org.apache.hadoop.io.Text; private void example(SalesDetail model1, SalesDetail model2) { Text code = model2.getStoreCode(); model1.setStoreCode(code); }
↓↑ほぼ同じ
import com.asakusafw.runtime.value.StringOption; private void example(SalesDetail model1, SalesDetail model2) { StringOption code = model2.getStoreCodeOption(); model1.setStoreCodeOption(code); }
また、getメソッドおよびAsStringの付いたgetメソッドは、内部で保持している値がnullだったらNullPointerExceptionが発生する。(メソッドの戻り値としてnullが返ってくるわけではない)
Optionの付いたsetメソッドではnullの状態ごとコピーされる。
同じStringOptionに対してgetAsString()を何度も呼ぶのも実行効率が悪い。
(TextからStringへ変換した結果をStringOption内部でキャッシュしていたりはしない為)
//実行効率が悪いし、値がnullだったらNullPointerExceptionが発生する public void example(SalesDetail model) { if (model.getItemCodeAsString().equals("0001")) { 〜 } else if (model.getItemCodeAsString().equals("0002")) { 〜 } else if (model.getItemCodeAsString().equals("0003")) { 〜 } }
↓
import com.asakusafw.runtime.value.StringOption;
private static final StringOption ITEM0001 = new StringOption("0001"); private static final StringOption ITEM0002 = new StringOption("0002"); private static final StringOption ITEM0003 = new StringOption("0003"); public void example(SalesDetail model) { if (model.getItemCodeOption().equals(ITEM0001)) { 〜 } else if (model.getItemCodeOption().equals(ITEM0002)) { 〜 } else if (model.getItemCodeOption().equals(ITEM0003)) { 〜 } }
StringOptionの定数を用意しておけば、変換(String→Text)の回数は定数の個数分だけで済む。
model.getXxxAsString()を使う方式だと、呼び出す度に変換(Text→String)が発生してしまう。
メソッド | ver | 説明 | 例 |
---|---|---|---|
new StringOption(String) | 文字列を保持する。 nullを渡しても良い。 |
StringOption option = new StringOption("abc"); |
|
new StringOption() | nullを保持する。 | StringOption option = new StringOption(); |
|
Text get() | Textを返す。 nullの場合はNPEが発生する。 |
||
String getAsString() | Stringを返す。 nullの場合はNPEが発生する。 |
String s = option.getAsString(); |
|
Text or(Text) | Textを返す。 nullの場合は引数のTextを返す。 |
||
String or(String) | Stringを返す。 nullの場合は引数のStringを返す。 →orの説明 |
String s = option.or(""); |
|
StringOption orOption(StringOption) | 0.9.1 | null以外の場合は自分自身を返す。[2017-04-30] nullの場合は引数のStringOptionを返す。 |
StringOption EMPTY = new StringOption(""); |
void reset() | 空文字列にする。 →resetの説明 |
option.reset(); |
|
boolean isNull() | 中身がnullの場合trueを返す。 | if (option.isNull()) { 〜 } |
|
boolean isPresent() | 0.9.1 | 中身がnull以外の場合trueを返す。[2017-04-30] | if (option.isPresent()) { 〜 } |
boolean isEmpty() | 0.9.1 | 中身が空文字列の場合trueを返す。[2017-04-30] nullの場合はNPEが発生する。 |
if (option.isEmpty()) { 〜 } |
boolean has(String) | 文字列が等しいかどうかを判定する。 (equalsはStringOptionを引数に取るが、hasはStringが引数) |
if (option.has("abc")) { 〜 } |
|
boolean contains(String) boolean contains(StringOption) |
0.9.1 | 文字列を含んでいるかどうかを判定する。[2017-04-30] 中身または引数がnullの場合はNPEが発生する。 |
if (option.contains("b")) { 〜 } |
boolean startsWith(String) boolean startsWith(StringOption) |
0.9.1 | 文字列の先頭が一致しているかどうかを判定する。[2017-04-30] 中身または引数がnullの場合はNPEが発生する。 |
if (option.startsWith("a")) { 〜 } |
boolean endsWith(String) boolean endsWith(StringOption) |
0.9.1 | 文字列の末尾が一致しているかどうかを判定する。[2017-04-30] 中身または引数がnullの場合はNPEが発生する。 |
if (option.endsWith("c")) { 〜 } |
void appendTo(StringBuilder) | 0.9.1 | StringBuilderに文字列を追加する。[2017-04-30] nullの場合はNPEが発生する。 (内部ではStringOptionUtil.appendを呼び出している) |
StringBuilder sb = new StringBuilder(); |
ちなみに、StringOptionに値をセットするメソッドとしてmodifyとcopyFromがあるが、非推奨。
java.lang.Stringにあるメソッドと同じ名前のメソッドは、同じような処理を行う。[2017-04-30]
ただし、Textクラスのまま処理を行うので、一旦Stringに変換して自分で処理するより高速なはず。
AsakusaFW 0.8.0で、StringOptionUtilというユーティリティークラスが用意された。[2016-12-10]
StringOption本体とStringOptionUtilの区分けは、概ね、StringOption内部の状態を変えないものはStringOption本体、中身を加工するメソッドはStringOptionUtilにある。
(getAsStringメソッドでStringを取得すれば何でも出来るわけだが、StringOption内部はTextで保持しているので、変換はなるべく避けたい。StringOptionUtilはTextのまま扱うようになっている)
import com.asakusafw.runtime.value.StringOptionUtil;
メソッド | ver | 説明 | 例 |
---|---|---|---|
Reader asReader(StringOption) | 0.8.0 | StringOptionを読むReaderを返す。 | |
void trim(StringOption option) | 0.8.0 | StringOptionの両端の空白を除去する。 (StringOptionがnullの場合はNPEが発生する) |
StringOptionUtil.trim(option); |
void append(target, contents) | 0.8.0 | targetの末尾にcontentsを付加する。 (targetの中身がnullの場合はNPEが発生する) (contentsがnullの場合はNPEが発生する) |
StringOption target = new StringOption("abc"); |
StringOption target = new StringOption("abc"); |
|||
0.9.1 | StringBuilder target = new StringBuilder("abc"); |
||
int countCodePoints(StringOption) | 0.9.1 | コードポイント数を返す。[2017-04-30] | int n = StringOptionUtil.countCodePoints(option); |
int parseInt(StringOption) | 0.9.1 | intに変換する。[2017-04-30] (StringOptionがnullの場合はNPEが発生する) |
int n = StringOptionUtil.parseInt(option); |
long parseLong(StringOption) | 0.9.1 | longに変換する。[2017-04-30] (StringOptionがnullの場合はNPEが発生する) |
long n = StringOptionUtil.parseLong(option); |
BigDecimal parseDecimal(StringOption) | 0.9.1 | BigDecimalに変換する。[2017-04-30] (StringOptionがnullの場合はNPEが発生する) |
BigDecimal n =
StringOptionUtil.parseDecimal(option); |
数値系Optionと同じく、StringOptionにもorメソッドがある。[2013-12-17]
String code = model2.getStoreCodeOption().or(""); model1.setStoreCodeAsString(code);
↓↑実行効率以外は同じ
private static final Text EMPTY_TEXT = new Text(); Text code = model2.getStoreCodeOption().or(EMPTY_TEXT); model1.setStoreCode(code);
orメソッドでは、StringOptionの中身がnullだったらorメソッドの引数が返る。
空文字列を入れるなら、resetメソッドの方が速いかも?[2013-12-17]
model1.setStoreCodeAsString("");
↓↑同じ
model1.getStoreCodeOption().reset();