Je lis des tas d’entiers séparés par des espaces ou des nouvelles lignes du standard en utilisant Scanner(System.in)
.
Y at-il un moyen plus rapide de le faire en Java?
Y at-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 de simplement envelopper le stream dans un BufferedInputStream et d’utiliser quelque chose comme Ssortingng.split
/ Integer.parseInt
.
Une petite comparaison:
Lecture de 17 mégaoctets (4233600 numéros) en utilisant ce code
Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) sum += scanner.nextInt();
a pris ma machine 3,3 secondes . alors que cet extrait
BufferedReader bi = new BufferedReader(new InputStreamReader(System.in)); Ssortingng line; while ((line = bi.readLine()) != null) for (Ssortingng numStr: line.split("\\s")) sum += Integer.parseInt(numStr);
a pris 0,7 secondes .
En bidouillant le code plus loin (itération sur la line
avec Ssortingng.indexOf
/ Ssortingng.subssortingng
), vous pouvez facilement le réduire à environ 0,1 seconde, mais je pense avoir répondu à votre question et je ne veux pas la transformer en un peu de golf de code.
J’ai créé une petite classe InputReader qui fonctionne exactement comme le scanner de Java, mais surpasse celle-ci en termes de vitesse, en fait, elle surpasse aussi le BufferedReader. Voici un graphique à barres qui montre les performances de la classe InputReader que j’ai créée en lisant différents types de données à partir de l’entrée standard:
Voici deux manières différentes de trouver la sum de tous les nombres provenant de System.in en utilisant la classe InputReader:
int sum = 0; InputReader in = new InputReader(System.in); // Approach #1 try { // Read all ssortingngs and then parse them to integers (this is much slower than the next method). Ssortingng strNum = null; while( (strNum = in.nextSsortingng()) != 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) { }
Vous pouvez lire System.in
de manière digitale par chiffre. Regardez cette réponse: https://stackoverflow.com/a/2698772/3307066 .
Je copie le code ici (à peine modifié). Fondamentalement, il lit des 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 d'env. 2 fois plus rapide que d'utiliser SsortingngTokenizer
, qui était déjà plus rapide que Ssortingng.split(" ")
. (Le problème consistait à lire 1 million d’entiers jusqu’à 1 million chacun.)
Du sharepoint vue de la programmation, cette classe personnalisée d’parsing et d’impression est bien meilleure que les classes intégrées de scanner et de tampon Buffer Java.
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(); } }