I have been researching different methods for saving and loading configuration settings for my application. I've looked into Preferences, JSON, Properties and XML but I think I've settled on using the Properties method for most of my application settings.
However, I'm not able to find any information on how to best save and load an ArrayList from that file. It seems there are only individual key/pair string combinations possible.
So my question is basically, is there a better way to do this? I have an ArrayList of Strings in my application that I need to be able to save and load. Can this be done with Properties or do I need to use a separate file just to hold this list and then read it in as an ArrayList (per line, perhaps)?
EDIT: I should mention, I would like to keep all config files as readable text so I am avoiding using Serialization.
You can use commas to place multiple values on the same key.
key:value1,value2,value3
Then split them using the split function of a string after reading them in which will give you a String[] array which can be turned into an ArrayList via Arrays.asList().
Here's a partial MCVE:
ArrayList<String> al = new ArrayList<>();
al.add("value1");
al.add("value2");
al.add("value3");
String values = al.toString();
//Substring used to get rid of "[" and "]"
prop.setProperty("name",values.substring(1,values.length() - 1);
I found that using the following combination worked perfectly in my case.
Save:
String csv = String.join(",", arrayList());
props.setProperty("list", csv);
This will create a String containing each element of the ArrayList, separated with a comma.
Load:
arrayList = Arrays.asList(csv.split(","));
Takes the csv String and splits it at each comma, adding the elements to the arrayList reference.
I've seen two approaches for writing lists to a Properties file. One is to store each element of the list as a separate entry by adding the index to the name of the property—something like "mylist.1", "mylist.2". The other is to make a single value of the elements, separated by a delimiter.
The advantage of the first method is that you can handle any value without worrying about what to do if the value contains the delimiter. The advantage of the second is that you can retrieve the whole list without iterating over all entries in the Properties.
In either case, you probably want to write a wrapper (or find a library) around the Properties object that adds methods to store and retrieve lists using whichever scheme you choose. Often these wrappers have methods to validate and convert other common data types, like numbers and URLs.
Related
I'm trying to create an android app where you can learn hard words its over 300 words. I'm wondering how I should store the data in java.
I have a text file where all the words are. I have split the text so I have one array with the words and another Array with the definitions, they have the same index. In an activity, I want to make it as clean as possible, because sometimes I need to delete an index and It's not efficient to that with an ArrayList since they all need to move down.
PS. I really don't wanna use a database like Firebase.
Instead of using two different arrays and trying to ensure that their order/indices are matched, you should consider defining your own class.
class Word {
String wordName;
String wordDefinition;
}
You can then make a collection of this using ArrayList or similar.
ArrayList<Word> wordList;
I know you were concerned about using an ArrayList due to the large number of words, however I think for your use case the ArrayList is fine. Using a database is probably overkill, unless if you want to put in the whole dictionary ;)
In any case, it is better to define your own class and use this as a "wildcard" to collection types which accept these. This link may give you some ideas of other feasible data types.
https://developer.android.com/reference/java/util/Collections
I personally would use a HashMap.
The reason for this is because you can set the key to be the word and the value to be the definition of the word. And then you can grab the definition of the word by doing something like
// Returns the definition of word or null if the word isn't in the hashmap
hashMapOfWords.getOrDefault(word, null);
Check out this link for more details on a HashMap
https://developer.android.com/reference/java/util/HashMap
I have my selenium code which pulls a list of names available in the list.
Now I want to store the same names as global for later use. Please help.
I have tried array process, it pulls only true with each line, but not the values.
List<WebElement> allText = driver.findElements(By.xpath("//*[#id='pnlLeftMenu']/table/tbody/tr/td[2]/table[2]/tbody/tr[3]/td/table/tbody/tr"));
int total = allText.size();
System.out.println(total);
for(int i=3;i<=total;i++)
{
CaselevelSigningCMs =driver.findElement(By.xpath("//*[#id='pnlLeftMenu']/table/tbody/tr/td[2]/table[2]/tbody/tr[3]/td/table/tbody/tr"+"["+i+"]"+"/td[2]")).getText();
System.out.println(CaselevelSigningCMs);
}
I should get the names like: Ranjit Nyk, Sudhanva G.... I have to verify those names in other pages in other class/method. CaselevelSigningCMs is a global variable, it pulls single item only. I need similar array defined as global so that it can pull multiple items.
CaselevelSigningCMs =driver.findElement(By.xpath("//*[#id='pnlLeftMenu']/table/tbody/tr/td[2]/table[2]/tbody/tr[3]/td/table/tbody/tr"+"["+i+"]"+"/td[2]")).getText();
See how this String CaselevelSigningCMs is defined. Just like that go for ArrayList
Lets say the string used is defined some where like
public String CaselevelSigningCMs = "";
Similarly define ArrayList
public List<String> collectedItems=new ArrayList<>();
in loop add get text of each element, something like
collectedItems.add(driver.findElement(By.xpath("//*[#id='pnlLeftMenu']/table/tbody/tr/td[2]/table[2]/tbody/tr[3]/td/table/tbody/tr"+"["+i+"]"+"/td[2]")).getText());
You can use Arraylist to store and retrieve them
ArrayList ts=new ArrayList();
store the objects in arraylist
ts.add();
You can use Stream.map() function in order to convert the list of WebElements to the List of Strings containing element text attribute in a single shot:
List<String> allText = driver.findElements(By.xpath("//*[#id='pnlLeftMenu']/table/tbody/tr/td[2]/table[2]/tbody/tr[3]/td/table/tbody/tr"))
.stream()
.map(WebElement::getText)
.collect(Collectors.toList());
allText.forEach(System.out::println);
I also don't really like your XPath expression, especially:
//* wildcard
these table[2] and tr[3]
wildcard expressions take longer time to execute and consume more resources, and your approach seems to be very dependent on DOM structure so I would recommend using XPath Axes as well as XPath Functions / Operators to make your expression as relative, robust and reliable as possible.
You can also consider using Table class of the HtmlElements framework for working with HTML tables
I'm implementing this in Java.
Symbol file Store data file
1\item1 10\storename1
10\item20 15\storename6
11\item6 15\storename9
15\item14 1\storename250
5\item5 1\storename15
The user will search store names using wildcards like storename?
My job is to search the store names and produce a full string using symbol data. For example:
item20-storename1
item14-storename6
item14-storename9
My approach is:
reading the store data file line by line
if any line contains matching search string (like storename?), I will push that line to an intermediate store result file
I will also copy the itemno of a matching storename into an arraylist (like 10,15)
when this arraylist size%100==0 then I will remove duplicate item no's using hashset, reducing arraylist size significantly
when arraylist size >1000
sort that list using Collections.sort(itemno_arraylist)
open symbol file & start reading line by line
for each line Collections.binarySearch(itemno_arraylist,itmeno)
if matching then push result to an intermediate symbol result file
continue with step1 until EOF of store data file
...
After all of this I would combine two result files (symbol result file & store result file) to present actual strings list.
This approach is working but it is consuming more CPU time and main memory.
I want to know a better solution with reduced CPU time (currently 2 min) & memory (currently 80MB). There are many collection classes available in Java. Which one would give a more efficient solution for this kind of huge string processing problem?
If you have any thoughts on this kind of string processing problems that too in Java would be great and helpful.
Note: Both files would be nearly a million lines long.
Replace the two flat files with an embedded database (there's plenty of them, I used SQLite and Db4O in the past): problem solved.
So you need to replace 10\storename1 with item20-storename1 because the symbol file contains 10\item20. The obvious solution is to load the symbol file into a Map:
String tokens=symbolFile.readLine().split("\\");
map.put(tokens[0], tokens[1]);
Then read the store file line by line and replace:
String tokens=storelFile.readLine().split("\\");
output.println(map.get(tokens[0])+'-'+tokens[1]));
This is the fastest method, though still using a lot of memory for the map. You can reduce the memory storing the map in a database, but this would increase the time significantly.
If your input data file is not changing frequently, then parse the file once, put the data into a List of custom class e.g. FileStoreRecord mapping your record in the file. Define a equals method on your custom class. Perform all next steps over the List e.g. for search, you can call contains method by passing search string in form of the custom object FileStoreRecord .
If the file is changing after some time, you may want to refresh the List after certain interval or keep the track of list creation time and compare against the file update timestamp before using it. If ifferent, recreate the list. One other way to manage the file check could be to have a Thread continuously polling the file update and the moment, it is updated, it notifies to refresh the list.
Is there any limitation to use Map?
You can add Items to Map, then you can search easily?
1 million record means 1M * recordSize, therefore it will not be problem.
Map<Integer,Item> itemMap= new HashMap();
...
Item item= itemMap.get(store.getItemNo());
But, the best solution will be with Database.
I've been able to read a four column text file into a hashmap and get it to write to a output file. However, I need to get the second column(distinct values) into a hashset and write to the output file. I've been able to create the hashset, but it is grabbing everything and not sorting. By the way I'm new, so please take this into consideration when you answer. Thanks
Neither HashSet nor HashMap are meant to sort. They're fundamentally unsorted data structures. You should use an implementation of SortedSet, such as TreeSet.
Some guesses, related to mr Skeets answer and your apparent confusion...
Are you sure you are not inserting the whole line in the TreeSet? If you are going to use ONLY the second column, you will need to split() the strings (representing the lines) into columns - that's nothing that's done automatically.
Also, If you are actually trying to sort the whole file using the second column as key, You will need a TreeMap instead, and use the 2:nd column as key, and the whole line as data. But that won't solve the splitting, it only to keep the relation between the line and the key.
Edit: Here is some terminology for you, you might need it.
You have a Set. It's a collection of other objects - like String. You add other objects to it, and then you can fetch all objects in it by iterating through the set. Adding is done through the method add()and iterating can be done using the enhanced for loop syntax or using the iterator() method.
The set doesn't "grab" or "take" stuff; You add something to the set - in this case a String - Not an array of Strings which is written as String[]
(Its apparently possible to add array to a TreeSet (they are objects too) , but the order is not related to the contents of the String. Maybe thats what you are doing.)
String key = splittedLine[1]; // 2:nd element
"The second element of the keys" doesn't make sense at all. And what's the duplicates you're talking about. (note the correct use of apostrophes... :-)
I want to have multiple keys (>1) for a single value in a properties file in my java application. One simple way of doing the define each key in separate line in property file and the same value to all these keys. This approach increases the maintainability of property file. The other way (which I think could be smart way) is define comma separated keys with the value in single line. e.g.
key1,key2,key3=value
Java.util.properties doesn't support this out of box. Does anybody did simillar thing before? I did google but didn't find anything.
--manish
I'm not aware of an existing solution, but it should be quite straightforward to implement:
String key = "key1,key2,key3", val = "value";
Map<String, String> map = new HashMap<String, String>();
for(String k : key.split(",")) map.put(k, val);
System.out.println(map);
One of the nice things about properties files is that they are simple. No complex syntax to learn, and they are easy on the eye.
Want to know what the value of the property foo is? Quickly scan the left column until you see "foo".
Personally, I would find it confusing if I saw a properties file like that.
If that's what you really want, it should be simple to implement. A quick first stab might look like this:
Open file
For each line:
trim() whitespace
If the line is empty or starts with a #, move on
Split on "=" (with limit set to 2), leaving you with key and value
Split key on ","
For each key, trim() it and add it to the map, along with the trim()'d value
That's it.
Since java.util.Properties extends java.util.Hashtable, you could use Properties to load the data, then post-process the data.
The advantage to using java.util.Properties to load the data instead of rolling your own is that the syntax for properties is actually fairly robust, already supporting many of the useful features you might end up having to re-implement (such as splitting values across multiple lines, escapes, etc.).