una - ¿Cuál es la forma más rápida de leer desde System.in en Java?
system.in java ejemplo (4)
Estoy leyendo un montón de enteros separados por espacio o saltos de línea del estándar en el uso de Scanner(System.in)
.
¿Hay alguna forma más rápida de hacer esto en Java?
¿Hay alguna forma más rápida de hacer esto en Java?
Sí. El escáner es bastante lento (al menos según mi experiencia).
Si no necesita validar la entrada, le sugiero que simplemente ajuste la secuencia en un BufferedInputStream y use algo como String.split
/ Integer.parseInt
.
Una pequeña comparación:
Lectura de 17 megabytes (4233600 números) usando este código
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext())
sum += scanner.nextInt();
tomó mi máquina 3.3 segundos . mientras este fragmento
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);
tomó 0.7 segundos .
Al desordenar el código aún más (iterando sobre la line
con String.indexOf
/ String.substring
) puedes bajarlo a aproximadamente 0.1 segundos con bastante facilidad, pero creo que he respondido tu pregunta y no quiero convertir esto en un poco de código de golf.
Desde el punto de vista de la programación, esta clase personalizada de Escaneo e Impresión es mucho mejor que las clases de escáner integrado en Java y Lector Buffered.
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();
}
}
Y la clase impresa personalizada puede ser la siguiente
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();
}
}
Puede leer System.in
en forma de dígito por dígito. Mire esta respuesta: https://.com/a/2698772/3307066 .
Copio el código aquí (apenas modificado). Básicamente, lee enteros, separados por cualquier cosa que no sea un dígito. (Créditos para el autor original)
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;
}
En mi problema, este código fue aprox. 2 veces más rápido que usar StringTokenizer
, que ya era más rápido que String.split(" ")
. (El problema consistía en leer 1 millón de enteros de hasta 1 millón cada uno).
InputReader una pequeña clase InputReader que funciona igual que el Escáner de Java, pero supera en velocidad en muchas magnitudes, de hecho, supera al BufferedReader también. Aquí hay un gráfico de barras que muestra el rendimiento de la clase InputReader que he creado leyendo diferentes tipos de datos desde la entrada estándar:
Aquí hay dos maneras diferentes de encontrar la suma de todos los números que vienen de System.in usando la clase InputReader:
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) { }