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.
Related
I am trying to build a Sudoku game with letters instead of numbers. There will be an empty box like the following:
Each little box will be filled with a letter such that all horizontal and all vertical letters form words. To help the user, I will give them 6 words which will work, but they have to figure out how to arrange the 6 words. An example of a correctly completed box:
For this version of Sudoku made for scrabble players, oxo is a valid word (I am using a txt file for list of valid words).
The problem I have is this: How can the program figure out all 3 letter words that fit together horizontally and vertically? (I will choose 6 words from this subset to output to user).
The txt file is stored in a set, so each word is a string element. I thought about looping through all words in the set, and breaking each word into an array of char. But this seems like a long shot. Do you have any ideas about how to generate all possible solutions?
As requested, here is the solution I came up with after a few hours of tinkering. It comprises of three main parts, the main class doing the work, a Word class and a Dictionary help class. (There is also a Loader for reading the word list from file.)
Loader:
public class Loader {
public List<String> load(String fileName) throws IOException {
List<String> wordList = new ArrayList<>();
InputStream is = getClass().getClassLoader().getResourceAsStream(fileName);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String currentLine = br.readLine();
while (currentLine != null) {
if (!"".equals(currentLine)) {
for (String word : currentLine.split(" ")) {
wordList.add(word);
}
}
currentLine = br.readLine();
}
br.close();
return wordList;
}
}
Word:
public class Word {
private final String word;
private final Character[] characters;
public Word(String word) {
this.word = word;
characters = new Character[3];
characters[0] = word.charAt(0);
characters[1] = word.charAt(1);
characters[2] = word.charAt(2);
}
public String getWord() {
return word;
}
public Character getCharacter(int index) {
return characters[index];
}
#Override
public String toString() {
return getWord();
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Word word1 = (Word) o;
if (word != null ? !word.equals(word1.word) : word1.word != null) return false;
// Probably incorrect - comparing Object[] arrays with Arrays.equals
return Arrays.equals(characters, word1.characters);
}
#Override
public int hashCode() {
int result = word != null ? word.hashCode() : 0;
result = 31 * result + Arrays.hashCode(characters);
return result;
}
}
The primary reason for the Word class is to pre compute the constituent characters.
Dictionary:
public class Dictionary {
private final List<Word> wordList;
private final Set<Word> wordSet;
private final Map<Character, List<Word>> firstLetterMap;
private final Map<Character, Word[]> firstLetterArrayMap;
public Dictionary(List<String> wordList) {
this.wordList = wordList.stream().map(Word::new).collect(toList());
this.wordSet = new HashSet<>();
wordSet.addAll(this.wordList);
this.firstLetterMap = this.wordList.stream()
.collect(groupingBy(
w -> w.getCharacter(0)
));
this.firstLetterArrayMap = new ConcurrentHashMap<>();
for (Map.Entry<Character, List<Word>> entry : firstLetterMap.entrySet()) {
Word[] ws = new Word[entry.getValue().size()];
entry.getValue().toArray(ws);
firstLetterArrayMap.put(entry.getKey(), ws);
}
}
public List<Word> getWords() {
return new ArrayList<>(wordList);
}
public Word[] getWordsArray(Character c) {
return Optional.ofNullable(firstLetterArrayMap.get(c)).orElseGet(() -> new Word[0]);
}
public boolean isValidWord(Word word) {
return wordSet.contains(word);
}
}
Not much to explain here. It contains the word list a map to look up words based on their first letter. It also has a a method for validating a word.
Main program:
public class Tester {
private final Loader loader;
public Tester() {
loader = new Loader();
}
public static void main(String[] args) {
new Tester().run2();
}
public void run2() {
Map<Set<Word>, String> validBoards = new ConcurrentHashMap<>();
try {
List<String> words = loader.load("scrabble-3-letter.txt");
Dictionary dictionary = new Dictionary(words);
List<Word> allWords = dictionary.getWords();
Map<Word, String> checked = new ConcurrentHashMap<>();
System.out.println("Start search...");
long start = System.currentTimeMillis();
allWords.parallelStream().forEach(w -> {
checked.put(w, "");
Word[] list1 = dictionary.getWordsArray(w.getCharacter(0));
Word[] list2 = dictionary.getWordsArray(w.getCharacter(1));
Word[] list3 = dictionary.getWordsArray(w.getCharacter(2));
for (int i = 0; i < list1.length; ++i) {
Word w1 = list1[i];
if (checked.get(w1) != null) {
continue;
}
for (int j = 0; j < list2.length; ++j) {
Word w2 = list2[j];
for (int k = 0; k < list3.length; ++k) {
Word w3 = list3[k];
if (evaluate(w1, w2, w3, dictionary)) {
validBoards.put(new HashSet<>(Arrays.asList(w1, w2, w3)), "");
}
}
}
}
});
long end = System.currentTimeMillis();
System.out.println("Found " + validBoards.size() + " unique boards in " + (end - start) + " ms");
String forPrint = validBoards.keySet().stream()
.map(ArrayList::new)
.map(this::boardToString)
.collect(Collectors.joining("\n"));
writeToFile(forPrint, ".\\scrabble-sudoku-output.txt");
} catch (IOException e) {
e.printStackTrace();
}
}
private void writeToFile(String data, String fileName) throws IOException {
BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
writer.write(data);
writer.close();
}
private boolean evaluate(Word first, Word second, Word third, Dictionary dictionary) {
Word firstV = buildVerticalWord(first, second, third, 0);
Word secondV = buildVerticalWord(first, second, third, 1);
Word thirdV = buildVerticalWord(first, second, third, 2);
return dictionary.isValidWord(first) && dictionary.isValidWord(second) && dictionary.isValidWord(third)
&& dictionary.isValidWord(firstV) && dictionary.isValidWord(secondV) && dictionary.isValidWord(thirdV)
&& boardUniqueness(first, second, third, firstV, secondV, thirdV);
}
private boolean boardUniqueness(Word w1, Word w2, Word w3, Word w4, Word w5, Word w6) {
Set<Word> checkDup = new HashSet<>();
checkDup.add(w1);
checkDup.add(w2);
checkDup.add(w3);
checkDup.add(w4);
checkDup.add(w5);
checkDup.add(w6);
return checkDup.size() == 6;
}
private static Word buildVerticalWord(Word first, Word second, Word third, int index) {
return new Word("" + first.getCharacter(index) + second.getCharacter(index) + third.getCharacter(index));
}
private String boardToString(List<Word> board) {
StringBuilder sb = new StringBuilder();
List<Word> sortedWords = new ArrayList<>(board);
sortedWords.add(buildVerticalWord(board.get(0), board.get(1), board.get(2), 0));
sortedWords.add(buildVerticalWord(board.get(0), board.get(1), board.get(2), 1));
sortedWords.add(buildVerticalWord(board.get(0), board.get(1), board.get(2), 2));
sortedWords.sort(Comparator.comparing(Word::getWord));
sb.append(sortedWords.stream().map(Word::getWord).collect(Collectors.joining(", ")));
return sb.toString();
}
}
This is the second attempt (hence run2()). The first was brute force, picking one word per line from the full list and then checking if the vertical words were valid. Needless to say, this approach was not very efficient. (My list contains 1347 words so that's 2474829630 combinations to check.)
The second approach, with the goal to reduce the number of combinations, is to only check combinations of rows which have a chance of being valid.
This is how it works:
We iterate over the full list of words. In each iteration, the selected word, 'w', is the first column. We then filter out three lists of possible rows based on the first, second and third letter of w.
These reduced lists are vastly smaller than the full list and we do an exhaustive search on these lists. For each of these combinations, the first column remains the same.
Evaluation checks that each of the 6 words is valid and that there are indeed 6 unique words. Any valid combination is saved as a Set, in a Map. The Set should make any duplicates be overwritten and the Map is necessary for concurrency.
Last step is to write to file. This seems to not work brilliantly so I would look into changing that.
In the loop, we keep track of which words, w, we have used. If w1 is the same as w, we skip it. This is because each (valid) combination of 6 words has two possible forms.
A A A
B B B
C C C
is the same as
A B C
A B C
A B C
EDIT
Finding all solutions takes time, but finding some solution can be done quickly. It requires two additional lines of code.
In the Dictionary constructor, add a shuffle on the primary word list after it has been mapped:
// ....
public Dictionary(List<String> wordList) {
this.wordList = wordList.stream().map(Word::new).collect(toList());
Collections.shuffle(this.wordList); // Shuffle here
this.wordSet = new HashSet<>();
wordSet.addAll(this.wordList);
// ....
This will make all subsequent word lists and collections shuffled, making each new run unique.
In the main loop of the program, in run2(), add a a return statement in the innermost loop when finding a valid board:
if (evaluate(w1, w2, w3, dictionary)) {
validBoards.put(new HashSet<>(Arrays.asList(w1, w2, w3)), "");
return; // Return here to end search
}
The reason why it's a return and not some other break is because we're inside a lambda (the outer loop is a Stream.forEach()) so this will exit the lambda expression.
My expectation was to find one (1) valid board with this change, however, for some reason I end up with around 1310 (exact number varies) results instead. It seems it will finish the complete outer loop once before stopping.
Happy holidays!
The problem with my code is, sometimes it reads and compares the string without any issue but again it also throws errors when comparing other strings. I think my comparing function isn't performing enough to the mark, where do I need to set the code efficiently so that my comparison function works efficiently?
can someone please suggest to me something? so far I have tried comparing two files using bufferedreader. my code works to some extent but at the same time encounters an error
"Exception in thread "main"
java.lang.StringIndexOutOfBoundsException:"
I have a few pictures which describe my problem intuitively. I think my findtarget function isn't accurate enough which is why it keeps throwing these exceptions
ERROR : click here to view the image.
NO-ERROR : click here to view the image.
and here is my two files which contains positive and negative keywords.
NEGITIVE : file extention is negi.txt
POSITIVE : file extention is posi.txt
here is the findtarget function which is used to compare the strings.
public static int findTarget(String target, String source)
{
int target_len = target.length();
int source_len = source.length();
int add = 0;
// this function check the character whether it is present.
for (int i = 0; i < source_len; ++i) // i is a varialbe used to count upto source_len.
{
int j = 0; // take another variable to count loops
//int[] k = new int[100];
while (add == 0)
{
if (j >= target_len) // count upto target length
{
break;
}
else if (target.charAt(j) != source.charAt(i + j))
{
break;
}
else
{
++j;
if (j == target_len)
{
add++; // this will return 1: true
}
}
}
}
return add;
//System.out.println(""+add);
}
here is my entire code just incase if you wanna run them.
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.io.FileWriter;
public class test {
public static int findTarget(String target, String source)
{
int target_len = target.length();
int source_len = source.length();
int add = 0;
// this function check the character whether it is present.
for (int i = 0; i < source_len; ++i) // i is a varialbe used to count upto source_len.
{
int j = 0; // take another variable to count loops
//int[] k = new int[100];
while (add == 0)
{
if (j >= target_len) // count upto target length
{
break;
}
else if (target.charAt(j) != source.charAt(i + j))
{
break;
}
else
{
++j;
if (j == target_len)
{
add++; // this will return 1: true
}
}
}
}
return add;
//System.out.println(""+add);
}
public static void main(String... args)
{
// function 1
//this variable can be called from any place inside this main function.
int testing1 = 0;
int testing2 = 0;
try {
//reads user review and store them inside source1
Scanner sc = new Scanner(System.in);
System.out.println("Enter your review: ");
String source1 = sc.nextLine();
//establising a file object
File file = new File("posi.txt");
BufferedReader br1 = new BufferedReader(new FileReader(file));
//establising a file object
File file2 = new File("negi.txt");
BufferedReader br2 = new BufferedReader(new FileReader(file2));
String target1; // using a string variable to read the content of the file posi.txt
while ((target1 = br1.readLine()) != null) //as long the condition is not null it will keep printing.
{
testing1 += test.findTarget(target1, source1); // supplying two arguments to findtarget function.
}
String target2; // using a string variable to read the content of the file negi.txt
while ((target2 = br2.readLine()) != null) //as long the condition is not null it will keep printing.
{
testing2 += test.findTarget(target2, source1); // supplying two arguments to findtarget function.
}
br1.close(); br2.close();
System.out.println("positive is:"+testing1 +"\nnegative is :"+testing2); //-not going to print now! :D-
System.out.println("\nthank you for your feedback! :)");
}
catch (IOException e)
{
System.out.println("file error!");
}
// this function is an area where it stores the return value inside a file called pos.txt
try
{
FileWriter myWriter = new FileWriter("pos.txt",true);
// using the true condition makes the line move to the next line.
myWriter.write(" "+testing1);
myWriter.close();
}
catch (IOException e)
{
System.out.println("An error occurred.");
}
// writing neg inside a file called neg.txt
try
{
FileWriter myWriter = new FileWriter("neg.txt",true);
// using the true condition makes the line move to the next line.
myWriter.write(" "+testing2);
myWriter.close();
}
catch (IOException e)
{
System.out.println("An error occurred.");
}
// to evaluate an output based on highest count.
if(testing1 > testing2)
System.out.println("it is positive");
else if (testing1 == testing2)
System.out.println("it is neutral");
else
System.out.println("it is negative");
}
}
finally, I was able to solve the problem by using one of string method known as "regionmatches". Note: make sure your positive and negative files are arranged in an alphabetical sequence. This will give you an accurate increment.
Github : use my link to download the positive and negative keywords.
public static int findTarget(String target, String source) //method/function
{
String sourcee = source;
String targett = target;
int source_len = sourcee.length();
int target_len = targett.length();
/*
**this function check the character whether it is present using one of string methond called "regionmatch"
**regionMatches(int toffset, String other, int ooffset,int len)
*/
int add = 0;
boolean foundIt = false;
for (int i = 0;i <= source_len - 1;i++)
{
if (sourcee.regionMatches(i, targett, 0, target_len))
{
foundIt = true;
break;
}
}
//checking
if(!foundIt)
{
// do nothing.
}
else
{
add++;
}
return add; //returns incrementation
}
You increment i to the lenght of source but you call
.
.
.
else if (target.charAt(j) != source.charAt(i + j))
.
.
.
which exceeds the lenght of source at some point.
Lets say i == source.length, then source.charAt(i + j) throws an exception as soon as j > 0.
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.
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 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)));