web-dev-qa-db-fra.com

Quel est le moyen le plus rapide de lire System.in en Java?

Je lis des tas d'entiers séparés par des espaces ou des sauts de lignes de la norme en utilisant Scanner(System.in).

Y at-il un moyen plus rapide de le faire en Java?

55
pathikrit

Existe-t-il un moyen plus rapide de le faire en Java?

Oui. Le scanner est assez lent (du moins selon mon expérience).

Si vous n'avez pas besoin de valider l'entrée, je vous suggère d'envelopper simplement le flux dans un BufferedInputStream et d'utiliser quelque chose comme String.split/Integer.parseInt.


Une petite comparaison:

Lecture 17 mégaoctets (numéros 4233600) utilisant ce code

Scanner scanner = new Scanner(System.in);
while (scanner.hasNext())
    sum += scanner.nextInt();

a pris sur ma machine 3,3 secondes . tandis que cet extrait

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);

a pris 0,7 secondes .

En modifiant davantage le code (en parcourant line avec String.indexOf/String.substring), vous pouvez le réduire facilement à environ 0,1 seconde, mais je pense avoir répondu à votre question et je ne veux pas transformer cela en un code de golf.

81
aioobe

Si vous demandez du point de vue de la programmation concurrentielle, où si la soumission n’est pas assez rapide, ce sera TLE.
Ensuite, vous pouvez vérifier la méthode suivante pour récupérer String à partir de System.in. J'ai pris l'un des meilleurs codeurs de Java (sites concurrents)

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();
}`
3
Lokesh Agrawal

J'ai créé une petite classe InputReader qui fonctionne comme le scanneur Java, mais le surpasse de plusieurs magnitudes en vitesse. Voici un graphique à barres illustrant les performances de la classe InputReader que j'ai créée en lisant différents types de données à partir d'une entrée standard:

enter image description here

Voici deux manières différentes de trouver la somme de tous les nombres provenant de System.in à l'aide de la classe 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) { }
3
will.fiset

Vous pouvez lire à partir de System.in, chiffre par chiffre. Regardez cette réponse: https://stackoverflow.com/a/2698772/3307066 .

Je copie le code ici (à peine modifié). En gros, il lit les nombres entiers, séparés par tout ce qui n’est pas un chiffre. (Crédits à l'auteur 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;
}

Dans mon problème, ce code était env. Deux fois plus rapide que StringTokenizer, qui était déjà plus rapide que String.split(" ").

1
jbarrameda

StringTokenizer est un moyen beaucoup plus rapide de lire une entrée de chaîne séparée par des jetons.

Consultez l'exemple ci-dessous pour lire une chaîne d'entiers séparés par un espace et les stocker dans une liste,

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()));
} 
1
Hetal Rachh

Du point de vue de la programmation, cette classe personnalisée Scan and Print est bien meilleure que les classes intégrées Java Scanner et 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();
}
}

Et la classe d'impression personnalisée peut être comme suit

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();
}

}
0
Paul92

Vous pouvez utiliser BufferedReader pour lire des données

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]);
    }

Et pour imprimer utiliser StringBuffer

    StringBuffer sb = new StringBuffer();
    for(int i=0;i<n;i++){
              sb.append(arr[i]+" "); 
            }
    System.out.println(sb);
0
Shywalker