I am reading bunch of integers separated by space or newlines from the standard in using Scanner(System.in).
Is there any faster way of doing this in Java?
Is there any faster way of doing this in Java?
Yes. Scanner is fairly slow (at least according to my experience).
If you don't need to validate the input, I suggest you just wrap the stream in a BufferedInputStream and use something like String.split / Integer.parseInt.
A small comparison:
Reading 17 megabytes (4233600 numbers) using this code
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext())
sum += scanner.nextInt();
took on my machine 3.3 seconds. while this snippet
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);
took 0.7 seconds.
By messing up the code further (iterating over line with String.indexOf / String.substring) you can get it down to about 0.1 seconds quite easily, but I think I've answered your question and I don't want to turn this into some code golf.
I created a small InputReader class which works just like Java's Scanner but outperforms it in speed by many magnitudes, in fact, it outperforms the BufferedReader as well. Here is a bar graph which shows the performance of the InputReader class I have created reading different types of data from standard input:
Here are two different ways of finding the sum of all the numbers coming from System.in using the InputReader class:
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) { }
If you asking from competitive programming point of view, where if the submission is not fast enough, it will be TLE.
Then you can check the following method to retrieve String from System.in.
I have taken from one of the best coder in java(competitive sites)
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();
}`
You can read from System.in in a digit by digit way. Look at this answer: https://stackoverflow.com/a/2698772/3307066.
I copy the code here (barely modified). Basically, it reads integers, separated by anything that is not a digit. (Credits to the original author.)
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;
}
In my problem, this code was approx. 2 times faster than using StringTokenizer, which was already faster than String.split(" ").
(The problem involved reading 1 million integers of up to 1 million each.)
StringTokenizer is a much faster way of reading string input separated by tokens.
Check below example to read a string of integers separated by space and store in 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()));
}
In programming perspective this customized Scan and Print class is way better than Java inbuilt Scanner and BufferedReader classes.
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();
}
}
And the customized Print class can be as follows
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();
}
}
You can use BufferedReader for reading data
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]);
}
And for printing use StringBuffer
StringBuffer sb = new StringBuffer();
for(int i=0;i<n;i++){
sb.append(arr[i]+" ");
}
System.out.println(sb);
Here is the full version fast reader and writer. I also used Buffering.
import java.io.*;
import java.util.*;
public class FastReader {
private static StringTokenizer st;
private static BufferedReader in;
private static PrintWriter pw;
public static void main(String[] args) throws IOException {
in = new BufferedReader(new InputStreamReader(System.in));
pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
st = new StringTokenizer("");
pw.close();
}
private static int nextInt() throws IOException {
return Integer.parseInt(next());
}
private static long nextLong() throws IOException {
return Long.parseLong(next());
}
private static double nextDouble() throws IOException {
return Double.parseDouble(next());
}
private static String next() throws IOException {
while(!st.hasMoreElements() || st == null){
st = new StringTokenizer(in.readLine());
}
return st.nextToken();
}
}
Reading from disk, again and again, makes the Scanner slow. I like to use the combination of BufferedReader and Scanner to get the best of both worlds. i.e. speed of BufferredReader and rich and easy API of the scanner.
Scanner scanner = new Scanner(new BufferedReader(new InputStreamReader(System.in)));
Related
Even though I have parsed it to an integer value I'm still getting an error. I need to get the integer value from a String input where I remove the comma and space, and store it in an array, then I convert that array to an integer array
import java.util.ArrayList;
import java.util.Scanner;
public class SeriesSolution {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int count = sc.nextInt();
ArrayList<Integer> modes = new ArrayList<>();
for (int x = 0; x < count; x++) {
String lines = sc.nextLine();
String[] strs = lines.split(", ");
int[] array = new int[strs.length];
for (int i = 0; i < strs.length; i++) {
if (Integer.parseInt(strs[i]) > 0 && Integer.parseInt(strs[i]) < 100) {
array[i] = Integer.parseInt(strs[i]);
}
}
modes.add(mode(array));
}
for (int y:modes){
System.out.println(y);
}
}
private static int mode(int a[]) {
int maxValue=0, maxCount=0;
for (int anA : a) {
int count = 0;
for (int anA1 : a) {
if (anA1 == anA) ++count;
}
if (count > maxCount) {
maxCount = count;
maxValue = anA;
}
}
return maxValue;
}
}
The issue is mainly because Scanner accepts Enter keystroke as input. And because of which
String lines = sc.nextLine();
this peice of code stores an empty string into lines variable. This empty string throws NumberFormatException when passed to parseInt()
I would recommend you to use BufferedReader with InputStreamReader
BufferedReader br = new BuffereedReader(new InputStreamReader(System.in));
This is good for larger inputs and is error free. Though empty checks are must as prevention is better.
If you want to use Scanner, I would recommend you to update the code and use the below snippet of code.
String lines = "";
while (lines.equals("")) {
lines = sc.nextLine();
}
Do a check before the parseInt
if (strs[i] != null && !"".equals(strs[i]) && Integer.parseInt(strs[i]) ...
Or surround it with a try catch to catch the NumberformatException that will happen if a string is inserted instead of a number
I have been trying to find the total number of tabs and single space characters with using the code below. So if i use this
if (c[i] == '\t') {
++tabcount;
}
it gives tabCount = 0, also if i want to get number of single space characters using this
if (c[i] == ' ') {
++singlescpacecount;
}
it gives total number of white spaces in the whole file.
Code for tabCount is
public static void TabCount(String filename) throws IOException{
int tabcount = 0;
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int readChars = 0;
while ((readChars = is.read(c)) != -1) {
for (int i = 0; i < readChars; ++i) {
if (c[i] == '\t') {
++tabcount;
}
}
}
System.out.println("The total number of tabcounts are :" + tabcount);
} finally {
is.close();
}
}
Thanks in advance.
At least part of the issue must be that the OP's input file does not contain tabs as expected. As noted by #Andreas, the basic code structure does count tabs. However, I had suggested ensuring the file is not iterated multiple times for counting various characters. Here is one implementation on how to do that. It is not optimal, but suggestive.
/**
* A class to accumulate results
*/
static class Results
{
private int tabs = 0;
private int spaces = 0;
public void incTabCount()
{
++tabs;
}
public void incSpaceCount()
{
++spaces;
}
public int getTabCount()
{
return tabs;
}
public int getSpaceCount()
{
return spaces;
}
#Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("tabs: ");
sb.append(tabs);
sb.append("\nspaces: ");
sb.append(spaces);
return sb.toString();
}
}
/**
* Iterate the file once, checking for all desired characters,
* and store in the Results object
*/
public static Results countInFile(String filename) throws IOException
{
// create results
Results res = new Results();
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int readChars = 0;
while ((readChars = is.read(c)) != -1) {
for (int i = 0; i < readChars; ++i) {
// see if we have a tab
if (c[i] == '\t') {
res.incTabCount();
}
// see if we have a space
if (c[i] == ' ') {
res.incSpaceCount();
}
}
}
} finally {
is.close();
}
return res;
}
public static void main(String[] args)
{
Results res;
try {
res = countInFile("f:/tmp/test.txt");
System.out.println(res);
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Input file (the tab is after the word "has" in the first line):
This line has\ta tab.
And there are 7 spaces?
Based upon an input file, the results are as expected:
tabs: 1
spaces: 7
Edit: as an aside, there is a modification that would be ultimately more testable. If one were to separate the File handling from the counting in an input stream, then one could more easily feed a known input to the system. For example, modifying the above slightly:
/**
* Counts in a stream the number of tabs and spaces, and returns
* the Results
*/
private static Results countInStream(InputStream is) throws IOException
{
// create results
Results res = new Results();
try {
byte[] c = new byte[1024];
int readChars = 0;
while ((readChars = is.read(c)) != -1) {
for (int i = 0; i < readChars; ++i) {
// see if we have a tab
if (c[i] == '\t') {
res.incTabCount();
}
// see if we have a space
if (c[i] == ' ') {
res.incSpaceCount();
}
}
}
}
finally {
}
return res;
}
This method may have a String passed to it:
String s = "This\thas\ttabs.\nAs well as spaces";
InputStream is = new ByteArrayInputStream(s.getBytes("UTF8"));
res = countInStream(is);
System.out.println(res);
Since it is now much easier to test the main logic, one can clearly see the base counting operates as expected. The countInFile method suggest above may be modified to open the InputStream from the file, and then call the countInStream(). Such an approach would reduce the debate about whether the logic of the method is at issue, or the contents sent to the method.
I'm attempting to solve one of CodeEval's challenges. Everytime I submit an answer, which works perfectly fine with Java 8(1.8.0_11) on my local system, I am thrown this exception. CodeEval uses Java 7.
Code:
import java.io.*;
public class ArmStrong
{
public static boolean howstrong(int str, int[] ong)
{
int count = 0;
for (int i = 0; i < ong.length; i++)
{
count += Math.pow(ong[i], ong.length);
}
return (count == str);
}
public static void bin(BufferedReader in)
throws IOException, NumberFormatException
{
String line;
while ((line = in.readLine()) != null && line.length() != 0)
{
int str = Integer.parseInt(line.trim());
String[] lne = line.split("");
int[] ong = new int[lne.length];
for (int i = 0; i < lne.length; i++)
{
ong[i] = Integer.parseInt(lne[i]);
}
System.out.println(howstrong(str, ong));
}
}
public static void main(String[] args)
throws FileNotFoundException, IOException
{
FileReader inc = new FileReader(args[0]);
BufferedReader in = new BufferedReader(inc);
bin(in);
}
}
My test file:
8538
153
9657
2446
407
6821
9474
4192
371
8208
7959
191
3348
370
1634
9192
5172
7543
6078
2908
Saving my test file with an extra newline char works fine as well.
Here is text from website:
Exception in thread "main" java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:504)
at java.lang.Integer.parseInt(Integer.java:527)
at Main.bin(Main.java:25)
at Main.main(Main.java:35)
Here is terminal output from my side:
false
true
false
false
true
false
true
false
true
true
false
false
false
true
true
false
false
false
false
false
I believe you'll find it's much simpler to implement with a Scanner and nextInt() then using a BufferedReader. But first, your armstrong check seems to be incorrect (and I found it easier to simplify it). So let's start there with,
public static boolean isArmstrong(int val) {
char[] chars = String.valueOf(val).toCharArray();
int count = 0;
for (int i = 0; i < chars.length; i++) {
int digit = chars[i] - '0';
count += Math.pow(digit, chars.length);
}
return (count == val);
}
Then we can use the Scanner in main() and test the numbers like,
Scanner scanner = null;
try {
File f = new File("/home/efrisch/test.txt");
scanner = new Scanner(f);
while (scanner.hasNextInt()) {
System.out.println(isArmstrong(scanner.nextInt()) ? "True"
: "False");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (scanner != null) {
scanner.close();
}
}
So far, I have this code, which, in summary, takes two text files and a specified block size in cmd and standardises the txt files, and then puts them into blocks based on the specified block size.
import java.io.*;
import java.util.*;
public class Plagiarism {
public static void main(String[] args) throws Exception {
//you are not using 'myPlag' anywhere, you can safely remove it
// Plagiarism myPlag = new Plagiarism();
if (args.length == 0) {
System.out.println("Error: No files input");
System.exit(0);
}
String foo = null;
for (int i = 0; i < 2; i++) {
BufferedReader reader = new BufferedReader(new FileReader(args[i]));
foo = simplify(reader);
// System.out.print(foo);
int blockSize = Integer.valueOf(args[2]);
List<String> list = new ArrayList<String>();
for (int k = 0; k < foo.length() - blockSize + 1; k++) {
list.add(foo.substring(k, k + blockSize));
}
// System.out.print(list);
}
}
public static String simplify(BufferedReader input)
throws IOException {
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = input.readLine()) != null) {
sb.append(line.replaceAll("[^a-zA-Z]", "").toLowerCase());
}
return sb.toString();
}
}
The next thing I would like to do is use Horner's polynomial accumulation method (with set value x = 33) to convert each of these blocks into a hash code. I am completely stumped on this and would appreciate some help from you guys!
Thanks for reading, and thanks in advance for any advice given!
Horner's method for hash generation is as simple as
int hash=0;
for(int i=0;i<str.length();i++)
hash = x*hash + str.charAt(i);
I've seen how to get a random line from a text file, but the method stated there (the accepted answer) is running horrendously slow. It runs very slowly on my 598KB text file, and still slow on my a version of that text file which has only one out of every 20 lines, at 20KB. I never get past the "a" section (it's a wordlist).
The original file has 64141 lines; the shortened one has 2138 lines. To generate these files, I took the Linux Mint 11 /usr/share/dict/american-english wordlist and used grep to remove anything with uppercase or an apostrophe (grep -v [[:upper:]] | grep -v \').
The code I'm using is
String result = null;
final Random rand = new Random();
int n = 0;
for (final Scanner sc = new Scanner(wordList); sc.hasNext();) {
n++;
if (rand.nextInt(n) == 0) {
final String line = sc.nextLine();
boolean isOK = true;
for (final char c : line.toCharArray()) {
if (!(constraints.isAllowed(c))) {
isOK = false;
break;
}
}
if (isOK) {
result = line;
}
System.out.println(result);
}
}
return result;
which is slightly adapted from Itay's answer.
The object constraints is a KeyboardConstraints, which basically has the one method isAllowed(char):
public boolean isAllowed(final char key) {
if (allAllowed) {
return true;
} else {
return allowedKeys.contains(key);
}
}
where allowedKeys and allAllowed are provided in the constructor. The constraints variable used here has "aeouhtns".toCharArray() as its allowedKeys with allAllowed off.
Essentially, what I want the method to do is to pick a random word that satisfies the constraints (e.g. for these constraints, "outvote" would work, but not "worker", because "w" is not in "aeouhtns".toCharArray()).
How can I do this?
You have a bug in your implementation. You should read the line before you choose a random number. Change this:
n++;
if (rand.nextInt(n) == 0) {
final String line = sc.nextLine();
To this (as in the original answer):
n++;
final String line = sc.nextLine();
if (rand.nextInt(n) == 0) {
You should also check the constraints before drawing a random number. If a line fails the constraints it should be ignored, something like this:
n++;
String line;
do {
if (!sc.hasNext()) { return result; }
line = sc.nextLine();
} while (!meetsConstraints(line));
if (rand.nextInt(n) == 0) {
result = line;
}
I would read in all the lines, save these somewhere and then select a random line from that. This takes a trivial amount of time because a single file of less than 1 MB is a trivial size these days.
public class Main {
public static void main(String... args) throws IOException {
long start = System.nanoTime();
RandomDict dict = RandomDict.load("/usr/share/dict/american-english");
final int count = 1000000;
for (int i = 0; i < count; i++)
dict.nextWord();
long time = System.nanoTime() - start;
System.out.printf("Took %.3f seconds to load and find %,d random words.", time / 1e9, count);
}
}
class RandomDict {
public static final String[] NO_STRINGS = {};
final Random random = new Random();
final String[] words;
public RandomDict(String[] words) {
this.words = words;
}
public static RandomDict load(String filename) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(filename));
Set<String> words = new LinkedHashSet<String>();
try {
for (String line; (line = br.readLine()) != null; ) {
if (line.indexOf('\'') >= 0) continue;
words.add(line.toLowerCase());
}
} finally {
br.close();
}
return new RandomDict(words.toArray(NO_STRINGS));
}
public String nextWord() {
return words[random.nextInt(words.length)];
}
}
prints
Took 0.091 seconds to load and find 1,000,000 random words.