クレジットカード番号文字列の文字を文字「X」でマスクしようとしました。次のように2つの関数を記述しました。2番目の関数はcommons.lang.StringUtils
クラスを使用します。両方の場合にかかる時間を見つけようとしました。
public static String maskCCNumber(String ccnum){
long starttime = System.currentTimeMillis();
int total = ccnum.length();
int startlen=4,endlen = 4;
int masklen = total-(startlen + endlen) ;
StringBuffer maskedbuf = new StringBuffer(ccnum.substring(0,startlen));
for(int i=0;i<masklen;i++) {
maskedbuf.append('X');
}
maskedbuf.append(ccnum.substring(startlen+masklen, total));
String masked = maskedbuf.toString();
long endtime = System.currentTimeMillis();
System.out.println("maskCCNumber:="+masked+" of :"+masked.length()+" size");
System.out.println("using StringBuffer="+ (endtime-starttime)+" millis");
return masked;
}
public static String maskCCNumberCommons(String ccnum){
long starttime = System.currentTimeMillis();
int total = ccnum.length();
int startlen=4,endlen = 4;
int masklen = total-(startlen + endlen) ;
String start = ccnum.substring(0,startlen);
String end = ccnum.substring(startlen+masklen, total);
String padded = StringUtils.rightPad(start, startlen+masklen,'X');
String masked = padded.concat(end);
long endtime = System.currentTimeMillis();
System.out.println("maskCCNumber:="+masked+" of :"+masked.length()+" size");
System.out.println("using Stringutils="+(endtime-starttime)+" millis");
return masked;
}
public static void ccNumberMaskingDemo() {
String mcard1="5555555555554444";
maskCCNumber(mcard1);
maskCCNumberCommons(mcard1);
}
これを実行すると、この結果が得られました
maskCCNumber:=5555XXXXXXXX4444 of :16 size
using StringBuffer=0 millis
maskCCNumber:=5555XXXXXXXX4444 of :16 size
using Stringutils=25 millis
Commons.StringUtilsが最初の関数のforloop + StringBufferよりも時間がかかる理由がわかりません。明らかに、間違った方法でapiを使用しています。
この場合、誰かがこのAPIを正しく使用する方法をアドバイスできますか?
まず、このような短期間のコードを測定すると、CPU /ライブラリ/その他が提供するタイミング解像度が最小であるため、正確な結果が得られないことがよくあります(つまり、通常、0msまたは同じ小さな値が表示されます。以上)。
次に、さらに重要なことに、これを最適化しないでください。 "時期尚早の最適化はすべての悪の根源です"そしてあなたが最適化したい数ミリ秒しかない場合、努力は完全に無駄になります。この単純なマスク方法の最適化についてリモートで考える前に、何百万ものクレジットカードをマスクする必要があります。
どうぞ。清潔で再利用可能:
/**
* Applies the specified mask to the card number.
*
* @param cardNumber The card number in plain format
* @param mask The number mask pattern. Use # to include a digit from the
* card number at that position, use x to skip the digit at that position
*
* @return The masked card number
*/
public static String maskCardNumber(String cardNumber, String mask) {
// format the number
int index = 0;
StringBuilder maskedNumber = new StringBuilder();
for (int i = 0; i < mask.length(); i++) {
char c = mask.charAt(i);
if (c == '#') {
maskedNumber.append(cardNumber.charAt(index));
index++;
} else if (c == 'x') {
maskedNumber.append(c);
index++;
} else {
maskedNumber.append(c);
}
}
// return the masked number
return maskedNumber.toString();
}
サンプル呼び出し:
System.out.println(maskCardNumber("1234123412341234", "xxxx-xxxx-xxxx-####"));
> xxxx-xxxx-xxxx-1234
System.out.println(maskCardNumber("1234123412341234", "##xx-xxxx-xxxx-xx##"));
> 12xx-xxxx-xxxx-xx34
幸運を。
ApacheStringUtilsの使用...
String ccNumber = "123232323767";
StringUtils.overlay(ccNumber, StringUtils.repeat("X", ccNumber.length()-4), 0, ccNumber.length()-4);
これはStringUtilsに基づく少しクリーンな実装ですが、実装と比較してどのように機能するかはわかりません。とにかく、「時期尚早の最適化」コメントは非常に有効なままです。
public static String maskNumber(final String creditCardNumber) {
final String s = creditCardNumber.replaceAll("\\D", "");
final int start = 4;
final int end = s.length() - 4;
final String overlay = StringUtils.repeat(MASK_CHAR, end - start);
return StringUtils.overlay(s, overlay, start, end);
}
これは答えではないことはわかっていますが、正規表現を使用してこれを1つのステップで解決できます
String replaced = originalCreditCardNo.replaceAll("\\b(\\d{4})(\\d{8})(\\d{4})", "$1XXXXXXXX$3");
説明:
import Java.util.Scanner;
class StringTest{
public static void main(String ar[]){
Scanner s=new Scanner(System.in);
System.out.println("enter account number");
String name=s.next();
char a[]=new char[name.length()];
for(int i=0;i<name.length();i++){
a[i]=name.charAt(i);
}
for(int i=1;i<name.length()-3;i++){
a[i]='*';
}
System.out.println("your account number");
for(int i=0;i<name.length();i++){
System.out.print(a[i]);
}
}
}
おそらく、これはStringUtils
がApache-commons.jar
ファイルからロードされる時間です。実際の実行時間ではありません。
実際の実行時間を計算するには、複数回実行して、2番目のミリ秒数を確認します。 3日から100日までかかります。
とにかく、フランクが言ったように、このレベルに最適化することはお勧めできません。
String utilsは、おそらく文字列を数回コピーします。たとえば、padded.concat(end);を実行する場合です。 jvmは、2つの連結文字列のサイズの新しい文字列を割り当ててコピーします。 StringBufferを使用する場合、バッファにはすでに場所が割り当てられており、連結された文字列がそこにコピーされているため、これらのコピーをすべて保存します。 StringBufferの方が速いと私には理解できますが、測定される時間は私が予想するよりもかなり長いようです。
読みにくくなりますが、これを行うことができます
final char[] ca = in.toCharArray();
Arrays.fill(ca, left, str.length - right, 'X');
return new String(ca)
私のマシンでGoogleCaliperを使用すると、StringBuilderまたはStringUtils.overlay +繰り返しアプローチを使用した場合の100ns以上と比較して、約20〜25nsが生成されます。
import static org.Apache.commons.lang3.StringUtils.overlay;
import static org.Apache.commons.lang3.StringUtils.repeat;
import Java.util.Arrays;
import org.Apache.commons.lang3.StringUtils;
import com.google.caliper.Param;
import com.google.caliper.Runner;
import com.google.caliper.SimpleBenchmark;
public class ArrayCopyVsStringBuild extends SimpleBenchmark {
public static void main(final String[] args) throws Exception {
Runner.main(ArrayCopyVsStringBuild.class, args);
}
@Param({ "1234567890123456", "1234567890" })
private String input;
@Param({ "0", "4" })
private int left;
@Param({ "0", "4" })
private int right;
public void timeArray(final int reps) {
for (int i = 0; i < reps; i++) {
final char[] masked = input.toCharArray();
Arrays.fill(masked, left, masked.length - right, 'X');
final String x = new String(masked);
x.toString();
}
}
public void timeStringBuilder(final int reps) {
for (int i = 0; i < reps; i++) {
final StringBuilder b = new StringBuilder(input.length());
b.append(input.substring(0, left));
for (int z = 0; z < input.length() - left - right; ++z) {
b.append('X');
}
b.append(input.substring(input.length() - right));
final String x = b.toString();
x.toString();
}
}
public void timeStringUtils(final int reps) {
for (int i = 0; i < reps; i++) {
final StringBuilder b = new StringBuilder(input.length());
b.append(input.substring(0, left));
b.append(repeat('x', input.length() - left - right));
b.append(input.substring(input.length() - right));
final String x = b.toString();
x.toString();
}
}
public void timeStringUtilsOverlay(final int reps) {
for (int i = 0; i < reps; i++) {
final int maskLength = input.length() - left - right;
final String x = overlay(input, repeat('x', maskLength), left,
maskLength + left);
x.toString();
}
}
}
以下のコードは、文字列の75%をマスクします。
public static String mask(String input) {
int length = input.length() - input.length()/4;
String s = input.substring(0, length);
String res = s.replaceAll("[A-Za-z0-9]", "X") + input.substring(length);
return res;
}