I am trying to know the best possible way to sort a file.txt lines in a Java collection.
Using orderedSet removes duplication and I don't want that.
PriorityQueue does the job but I need my class to be Iterable and using PriorityQueue.Iterator does not give sorted results.
Now I am confused with using Arrays.sort or going with this approach:
using PriorityQueue when reading lines from text then copying the final Queue on an array to use its Iterator?
public class FileSorter implements Iterable<String> {
// this sorted set contains the lines
private PriorityQueue<String> lines0 = new PriorityQueue<>() ;
private ArrayList<String> lines = new ArrayList<>();
public void readFiles (String[] filePaths) throws IOException {
BufferedReader buf = null;
String line ;
for (String path:filePaths) {
//opening the file
buf = new BufferedReader(new FileReader(new File(path)));
//iterating through the lines and adding them the collection
while ((line = buf.readLine()) != null) {
if(line.trim().length() > 0) { //no blank lines
lines0.add(line);
}
}
};
//closing the buffer
buf.close();
while (!lines0.isEmpty()){
lines.add(lines0.poll());
}
}
public Iterator<String> iterator() {
return lines.iterator();
}
}
Thank you.
I think implementing Iterable is not the best approach because you should prefer composition over inheritance, and it's 2017 after all; no one implements their own collection classes anymore. That said, how about the following?
public class Main {
public static void main(String[] args) throws IOException, URISyntaxException {
for (String line : new FileSorter(new File(Main.class.getResource("test.txt").toURI()).toPath())) {
System.out.println(line);
}
}
static class FileSorter implements Iterable<String> {
private final Path path;
FileSorter(Path path) {
this.path = path;
}
#Override
public Iterator<String> iterator() {
try {
return Files.lines(path)
.sorted()
.iterator();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
}
Given a file test.txt in the same dir as the class Main:
a
b
a
c
The above program prints:
a
a
b
c
Iterable has different semantic than Stream because the former can be reused, while the latter can only be used once (until a terminal operation). Thus, my implementation reads the file every time you call iterator(). I didn't attempt to optimize it because you didn't ask for it, and premature optimization is the root of all evil.
Related
I'm making a Java program that needs to read info from a text file and then store it in an array and pass it to another class when called. My issue is that I can't seem to call it due to the IOException needed in the file reader class.
This is the main class that is supposed to call the fileReader.
public class window {
public static void main(String[] args){
String[] people = readFromText.read("people.txt");
}
}
File Reader Class
public class readFromText{
public static String[] read(String textFile) throws IOException {
BufferedReader inputFile = new BufferedReader(new
FileReader(textFile));
String[] array = new String[10];
String line = inputFile.readLine().toString();
int cnt = 0;
while (line!=null){
array[cnt] = line;
line = inputFile.readLine().toString();
cnt++;
}
inputFile.close();
return array;
}
}
Is it possible to do this, this way?
Firstly your code is not correct. You can not return the String[] array for the function need String[][].
Secondly for problem about exception you just need to catch it in your main class.
try {
String[] people = readFromText.read("people.txt");
} catch (IOException e) {
e.printStackTrace();
}
quick question about creating objects when given a Class object. Or maybe I need to go about this differently. First off my plan, I am writing a method that will take an array of File objects, and read each one into a Set, where each set is then appended to a list and the list returned. Below is what I have:
private static List<Set<String>> loadFiles(File[] files, Class whatType, Charset charSet){
List<Set<String>> setList = new ArrayList<Set<String>>(files.length);
try {
for(File f : files){
BufferedInputStream bs = new BufferedInputStream(new FileInputStream(f));
InputStreamReader r = new InputStreamReader(bs, charSet);
BufferedReader br = new BufferedReader(r);
Set<String> set = new HashSet<>(); //This is the problem line
String line = null;
while( (line = br.readLine()) != null){
set.add(line.trim());
}
br.close();
setList.add(set);
}
return setList;
} catch (FileNotFoundException e) {
//Just return the empty setlist
return setList;
} catch (IOException e) {
//return a new empty list
return new ArrayList<Set<String>>();
}
}
But what I want is to allow the user of the method to specify the type of Set to instantiate (as long as it contains Strings of course). That is what the 'whatType' param is for.
All my research has lead me to how to instantiate an object given the class name, but that is not really what I am after here.
If you can use Java8, you can solve this problem easily. Declare the method as follows:
private static List<Set<String>> loadFiles(File[] files, Supplier<Set> setSupplier, Charset charSet)
Change your problem line to:
Set<String> set = setSupplier.get();
Then, in each call to this method, the setSupplier param can be easily provided using method references: HashSet::new, TreeSet::new...
How about using Class.newInstance() method? I coded a simple example for you:
public <T extends Set> void myMethod(Class<T> type) {
T object;
try {
object = type.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public void caller() {
myMethod(HashSet.class);
}
Is this what you are looking for?
If you assume the class has a no-argument accessible constructor, you're basically a newInstance() call away:
Set<String> set = (Set<String) whatType.newInstance();
Note that if you define whatType as a Class<? extends Set> instead of just a raw Class, you can get rid of this ugly cast too.
I have a problem with the code below. I'm getting different results base on where the line list = new ArrayList<InClass>(); is declared. In place //B but everything works fine when I add it to //A and I cannot understand the difference. Here is the code:
import java.util.*;
import java.io.*;
public class ArrayListOne {
private ArrayList<InClass> list;
private InClass in;
public static void main(String args[]) {
ArrayListOne a = new ArrayListOne();
a.readFile();
}
public void readFile() {
//A
/**
* adding "list = new ArrayList<InClass>();"
* getting all 4 lines of test.txt
*/
try {
File file = new File("test.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));
String line = null;
while ((line = reader.readLine()) != null) {
assignToObject(line);
}
} catch (Exception ex) {
ex.printStackTrace();
}
readObject();
}
public void assignToObject(String s) {
//B
/**
* adding "list = new ArrayList<InClass>();"
* getting just last line of test.txt
*/
InClass n = new InClass(s);
list.add(n);
System.out.println(list.size());
}
public void readObject() {
for (int i=0; i<list.size(); i++) {
in = list.get(i);
System.out.println(in.stTest);
}
}
//inner class
public class InClass {
String stTest;
public InClass(String s) {
stTest = s;
}
}
}
the test.txt has 3 lines. in //A, I'm getting all three lines (what I want) but in //B I just get the last line.
It's easier to see the difference if you "inline" assignToObject() by copy-pasting the contents of assignToObject() to the proper place in readFile():
public void readFile() {
// B
// list = new ArrayList<InClass>();
try {
File file = new File("test.txt");
BufferedReader reader = new BufferedReader(new FileReader(file));
String line = null;
while ((line = reader.readLine()) != null) {
// Here is where assignToObject() was //
// B
// list = new ArrayList<InClass>();
InClass n = new InClass(line);
list.add(n);
System.out.println(list.size());
}
} catch (Exception ex) {
ex.printStackTrace();
}
readObject();
}
Now think about if you put list = new ArrayList<InClass>() in A and B.
If you declare list = new ArrayList<InClass>() at A (i.e. inside readFile()), the statement will be executed once -- when readFile() is called in main(). So you'll end up with one ArrayList containing everything you need.
However, if you declare list = new ArrayList<InClass>() at B (i.e. inside assignToObject()), you'll get a new list for every line you read (i.e. every time you call assignToObject()). This means that every iteration you'll end up with a new ArrayList that only contains the most recently read line. The ArrayList containing the previous line was thrown away, as the reference that used to point to it now points to a new object.
I have following method:
public String exportAsCsv(CqlQuery query) {
Iterator<String> result = queryService.execute(.....);
StringBuilder buf = new StringBuilder();
for (String nextLine : result) {
buf.append(nextLine);
}
return buf.toString();
}
It executes some query which returns Iterator<String> - it contains gigabytes of data, so appending it to StringBuilder is not the best idea...
I would like to change my method so that it returns InputStream instead.
This could be one possible implementation (pseudo code):
public InputStream exportAsCsv(CqlQuery query) {
final Iterator<String> result = queryService.execute(query,false);
return new MagicalInputStream(){
#Overwrite
byte[] readNext() {
if(!result.hasNext()) {
return null;
} else {
return result.next().getBytes();
}
}
}
}
I am looking for InputStream where I have to implement abstract method (like byte[] readNext()), which will be used to read data chunks - one by one. So this input stream has to buffer read chunk, stream it back, and when its buffer is empty it should read next chunk.
The idea is, that I read next elements from Iterator ONLY when "client" rads next bytes from input stream.
Or there might be another possibility to change my method so that it does return InputStream instead of String - any ideas?
The whole InputStream implementation could be avoided if you allow your method to accept an java.io.Writer. Instead of appending Strings to the in-memory StringBuilder, you append them to the provided Writer.
public void exportAsCsv(CqlQuery query, Writer writer) {
Iterator<String> result = queryService.execute(.....);
for (String nextLine : result) {
writer.append(nextLine);
}
}
If you really want an InputStream, though, you could try something like this:
public InputStream exportAsCsv(CqlQuery query) {
Iterator<String> result = queryService.execute(.....);
return new SequenceInputStream(asStreamEnum(result));
}
private Enumeration<InputStream> asStreamEnum(final Iterator<String> it) {
return new Enumeration<InputStream>() {
#Override
public boolean hasMoreElements() {
return it.hasNext();
}
#Override
public InputStream nextElement() {
try {
return new ByteArrayInputStream(it.next().getBytes("UTF-8"));
} catch (UnsupportedEncodingException ex) {
throw new RuntimeException(ex);
}
}
};
}
I haven't actually tested this approach yet, so be warned; conceptually, though, I think this is what you're after.
I want to save the contents of my arraylist to a textfile. What I have so far is shown below, however instead of adding x.format("%s%s", "100", "control1"); to the textfile, I want to add objects from an arraylist, how do I go about this?
import java.util.*;
public class createfile
{
ArrayList<String> control = new ArrayList<String>();
private Formatter x;
public void openFile()
{
try {
x = new Formatter("ControlLog.txt");
} catch (Exception e) {
JOptionPane.showMessageDialog(null, "Error: Your file has not been created");
}
}
public void addRecords()
{
x.format("%s%s", "100", "control1");
}
public void closeFile()
{
x.close();
}
}
public class complete
{
public static void main(String[] args)
{
createfile g = new createfile();
g.openFile();
g.addRecords();
g.closeFile();
}
}
Both ArrayList and String implement Serializable. Since you have an ArrayList of string you can write it to the file like this:
FileOutputStream fos = new FileOutputStream("path/to/file");
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(myArrayList); //Where my array list is the one you created
out.close();
Here is a really good tutorial that shows you how to write java objects to a file.
The written objects can be read back from the file in a similar way.
FileInputStream in = new FileInputStream("path/to/file");
ObjectInputStream is = new ObjectInputStream(in);
myArrayList = (ArrayList<String>) is.readObject(); //Note that you will get an unchecked warning here
is.close()
Here is a tutorial on how to read objects back from a file.