Scanner(System.in)
を使用して、標準からスペースまたは改行で区切られた整数の束を読んでいます。
Javaでこれを行うより速い方法はありますか?
Javaでこれを行うより速い方法はありますか?
はい。スキャナーはかなり遅いです(少なくとも私の経験では)。
入力を検証する必要がない場合は、BufferedInputStreamでストリームをラップし、String.split
/Integer.parseInt
のようなものを使用することをお勧めします。
小さな比較:
このコードを使用して17メガバイト(4233600の数字)を読み取る
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext())
sum += scanner.nextInt();
私のマシンにかかった.3秒。このスニペット
BufferedReader bi = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = bi.readLine()) != null)
for (String numStr: line.split("\\s"))
sum += Integer.parseInt(numStr);
.7秒かかりました。
さらにコードを台無しにすることで(line
をString.indexOf
/String.substring
で繰り返す)、0.1秒程度まで簡単に短縮できますが、あなたの質問に答えて、これをいくつかのコードゴルフに変えたくありません。
競争的なプログラミングの観点から尋ねると、提出が十分に速くない場合、TLEになります。
次に、System.inから文字列を取得する次のメソッドを確認できます。私はJava(競争サイト)で最高のコーダーの1つから取った
private String ns()
{
int b = skip();
StringBuilder sb = new StringBuilder();
while(!(isSpaceChar(b))){ // when nextLine, (isSpaceChar(b) && b != ' ')
sb.appendCodePoint(b);
b = readByte();
}
return sb.toString();
}`
私は小さな InputReader クラスを作成しました。このクラスはJavaのScannerと同じように機能しますが、速度は大幅に向上します。実際、BufferedReaderよりも優れています。標準入力からさまざまな種類のデータを読み取って作成したInputReaderクラスのパフォーマンスを示す棒グラフを次に示します。
InputReaderクラスを使用して、System.inからのすべての数値の合計を見つける2つの異なる方法を次に示します。
int sum = 0;
InputReader in = new InputReader(System.in);
// Approach #1
try {
// Read all strings and then parse them to integers (this is much slower than the next method).
String strNum = null;
while( (strNum = in.nextString()) != null )
sum += Integer.parseInt(strNum);
} catch (IOException e) { }
// Approach #2
try {
// Read all the integers in the stream and stop once an IOException is thrown
while( true ) sum += in.nextInt();
} catch (IOException e) { }
_System.in
_から1桁ずつ読み取ることができます。この答えを見てください: https://stackoverflow.com/a/2698772/3307066 。
ここにコードをコピーします(ほとんど変更しません)。基本的に、数字ではないもので区切られた整数を読み取ります。 (元の著者へのクレジット。)
_private static int readInt() throws IOException {
int ret = 0;
boolean Dig = false;
for (int c = 0; (c = System.in.read()) != -1; ) {
if (c >= '0' && c <= '9') {
Dig = true;
ret = ret * 10 + c - '0';
} else if (Dig) break;
}
return ret;
}
_
私の問題では、このコードは約でした。 StringTokenizer
を使用するよりも2倍高速です。これは既にString.split(" ")
よりも高速でした。 (問題には、それぞれ最大100万個の100万個の整数の読み取りが含まれていました。)
StringTokenizer
は、トークンで区切られた文字列入力を読み取るためのはるかに高速な方法です。
以下の例をチェックして、スペースで区切られた整数の文字列を読み取り、arraylistに保存します。
String str = input.readLine(); //read string of integers using BufferedReader e.g. "1 2 3 4"
List<Integer> list = new ArrayList<>();
StringTokenizer st = new StringTokenizer(str, " ");
while (st.hasMoreTokens()) {
list.add(Integer.parseInt(st.nextToken()));
}
プログラミングの観点では、このカスタマイズされたScanおよびPrintクラスは、Java組み込みのScannerおよびBufferedReaderクラスよりもはるかに優れています。
import Java.io.InputStream;
import Java.util.InputMismatchException;
import Java.io.IOException;
public class Scan
{
private byte[] buf = new byte[1024];
private int total;
private int index;
private InputStream in;
public Scan()
{
in = System.in;
}
public int scan() throws IOException
{
if(total < 0)
throw new InputMismatchException();
if(index >= total)
{
index = 0;
total = in.read(buf);
if(total <= 0)
return -1;
}
return buf[index++];
}
public int scanInt() throws IOException
{
int integer = 0;
int n = scan();
while(isWhiteSpace(n)) /* remove starting white spaces */
n = scan();
int neg = 1;
if(n == '-')
{
neg = -1;
n = scan();
}
while(!isWhiteSpace(n))
{
if(n >= '0' && n <= '9')
{
integer *= 10;
integer += n-'0';
n = scan();
}
else
throw new InputMismatchException();
}
return neg*integer;
}
public String scanString()throws IOException
{
StringBuilder sb = new StringBuilder();
int n = scan();
while(isWhiteSpace(n))
n = scan();
while(!isWhiteSpace(n))
{
sb.append((char)n);
n = scan();
}
return sb.toString();
}
public double scanDouble()throws IOException
{
double doub=0;
int n=scan();
while(isWhiteSpace(n))
n=scan();
int neg=1;
if(n=='-')
{
neg=-1;
n=scan();
}
while(!isWhiteSpace(n)&& n != '.')
{
if(n>='0'&&n<='9')
{
doub*=10;
doub+=n-'0';
n=scan();
}
else throw new InputMismatchException();
}
if(n=='.')
{
n=scan();
double temp=1;
while(!isWhiteSpace(n))
{
if(n>='0'&&n<='9')
{
temp/=10;
doub+=(n-'0')*temp;
n=scan();
}
else throw new InputMismatchException();
}
}
return doub*neg;
}
public boolean isWhiteSpace(int n)
{
if(n == ' ' || n == '\n' || n == '\r' || n == '\t' || n == -1)
return true;
return false;
}
public void close()throws IOException
{
in.close();
}
}
カスタマイズされたPrintクラスは次のようになります
import Java.io.BufferedWriter;
import Java.io.IOException;
import Java.io.OutputStreamWriter;
public class Print
{
private BufferedWriter bw;
public Print()
{
this.bw = new BufferedWriter(new OutputStreamWriter(System.out));
}
public void print(Object object)throws IOException
{
bw.append("" + object);
}
public void println(Object object)throws IOException
{
print(object);
bw.append("\n");
}
public void close()throws IOException
{
bw.close();
}
}
BufferedReaderを使用してデータを読み取ることができます
BufferedReader inp = new BufferedReader(new InputStreamReader(System.in));
int t = Integer.parseInt(inp.readLine());
while(t-->0){
int n = Integer.parseInt(inp.readLine());
int[] arr = new int[n];
String line = inp.readLine();
String[] str = line.trim().split("\\s+");
for(int i=0;i<n;i++){
arr[i] = Integer.parseInt(str[i]);
}
また、印刷にはStringBufferを使用します
StringBuffer sb = new StringBuffer();
for(int i=0;i<n;i++){
sb.append(arr[i]+" ");
}
System.out.println(sb);