No such element in map, why? - java

I am working on a code in which if I try arrays everything works fine but when i try to solve that example with list it displays
Exception in thread "main" java.util.NoSuchElementException
at java.util.AbstractList$Itr.next(Unknown Source)
at com.delete.files.DeleatingFiles.check(DeleatingFiles.java:27)
at com.delete.files.DeleatingFiles.main(DeleatingFiles.java:60)
and the code is :
Map<String, String> map = new HashMap<String, String>();
File folder = new File("F://fileIO/");
if (folder.isDirectory()) {
List<File> filesName = Arrays.asList(folder.listFiles());
Iterator<File> itList = filesName.listIterator();
while (itList.hasNext()) {
map.put(itList.next().getName(), itList.next().toString());
}
System.out.println(map);
}
} else {
System.err.println("something is wrong");
}
}
EDIT 1: All I am trying is to save file name with absolute path as key value pair.
EDIT 2: can't use as Stringnext=itrList.next() as Iterator is of File type.
Now , can anyone tell me the cause of problem ??
Please tell me if there is something wrong.
Thanks.

Your code calls next twice, so if itList has an odd number of elements, the last call would result in NoSuchElementException.
Here is how you can fix your code:
while (itList.hasNext()) {
// Call "next()" once
File next = itList.next();
// Use "next" as many times as you need
map.put(next.getName(), next.toString());
}
is there any other way i can do that without using array?
You can simplify iteration considerably by switching to "for-each" loop:
if (folder.isDirectory()) {
for (File file : folder.listFiles()) {
map.put(file.getName(), file.toString());
}
System.out.println(map);
}
Now your code does not create unnecessary copies of lists, and is free of the NoSuchElementException bug.

In your code map.put(itList.next().getName(), itList.next().toString()); call next() twice even you check itList.hasNext() once.
while (itList.hasNext()) { // Check once
map.put(itList.next().getName(), itList.next().toString()); // next() Call twice here
}
Your code may have to be corrected as like follows
Map<String, String> map = new HashMap<String, String>();
File folder = new File("F://fileIO/");
if (folder.isDirectory()) {
List<File> filesName = Arrays.asList(folder.listFiles());
Iterator<File> itList = filesName.listIterator();
File file;
while (itList.hasNext()) {
file = itList.next();
map.put(file.getName(), file.toString());
}
System.out.println(map);
}
else {
System.err.println("something is wrong");
}

as said, you were calling next twice in each loop. It can also be resolved by using java-8 streams.
Map<String, String> map = new HashMap<String, String>();
File folder = new File("F://fileIO/");
if (folder.isDirectory()) {
map = Arrays.asList(folder.listFiles())
.stream()
.collect(Collectors.toMap(File::getName,
Object::toString));
System.out.println(map);
} else {
System.err.println("Something is wrong!");
}
Map<String, String> map = folder.isDirectory ?
map = Arrays.asList(folder.listFiles())
.stream()
.collect(Collectors.toMap(File::getName,
Object::toString)) :
new HashMap<String, String>();

Related

How to parse the csv to hashmap grouping by column using opencsv

I am trying to use hashmap the col1 values to col2 values from a csv file using CSVREADER. But I am unable to find a logic to do so.
I want to do it through reading the CSV through CSVReader, looping the datalines and using arraylist and hashmap key and value(arraylist). I dont want to hardcode it..
I did something till the following. Unable to proceed further. Please help..
CSVReader csvReader = new CSVReader(new FileReader(fileName),',','"',1);
Map<String, List<String>> tableandcols = new HashMap<String, List<String>>();
ArrayList<String> tablenames = new ArrayList<>();
ArrayList<String> colnames = new ArrayList<>();
while((row = csvReader.readNext()) != null) {
tablenames.add(row[0]);
colnames.add(row[1]);
}
input data:
State,City,Country
NJ,Trenton,US
NJ,Newark,US
NC,Cary,US
NC,Charlotte,US
GA,Atlanta,US
I want the data to be in hashmap as following
[<NJ,[Trenton,Newark]>
<NC,[Cary,Charlotte]>
<GA,[Atlanta]>]
You can try below piece of code :
try
{
CSVReader csvReader = new CSVReader(new FileReader(fileName),',','"',1);
Map<String, List<String>> tableandcols = new HashMap<String, List<String>>();
while((row = csvReader.readNext()) != null)
{
// If map contains state already, add the city to the values list
if(tableandcols.containsKey(row[0]))
{
tableandcols.get(row[0]).add(row[1);
}
// if map doesn't have this state as key, insert a key and value
else {
List<String> cities = new ArrayList<>();
cities.add(row[1]);
tableandcols.put(row[0], cities);
}
}
}
catch(Exception e){
// log exception
}
Alternatively, you can also use HeaderColumnNameTranslateMappingStrategy to map column values to java bean. Loop through the java beans list and aggregate cities based on state.
You can simply do it by using java-8 stream approach, use readAll to read complete file in List<String[]>
Reads the entire file into a List with each element being a String[] of tokens. Since the current implementation returns a LinkedList, you are strongly discouraged from using index-based access methods to get at items in the list. Instead, iterate over the list.
If you want to skip first row with headers then use skip(1), and then use Collectors.groupingBy to group the elements based on State
Map<String, List<String>> res = arr.stream().skip(1)
.collect(Collectors.groupingBy(str -> str[0], Collectors.mapping(str -> str[1], Collectors.toList())));
Or simple for loop using map.compute
List<String[]> arr = csvReader.readAll();
Map<String, List<String>> tableandcols = new HashMap<String, List<String>>();
for(String[] array : arr) {
tableandcols.compute(array[0], (key,val)->val==null ? new ArrayList<>() : val).add(array[1]);
}

If condition for Yaml file- Java

I have the Yaml file:
#Define CDN domains
---
CDN:
quality: 200..300
cost: low
Video-type: mp4
and with this Java code, I retrieve sub values of CDN:
// The path of your YAML file.
Yaml yaml = new Yaml();
Map<String, Map<String, String>> values =
(Map<String, Map<String, String>>) yaml
.load(new FileInputStream(new File("/workspace/servlet-yaml/src/test.yaml")));
for (String key : values.keySet()) {
Map<String, String> subValues = values.get(key);
for (String subValueKey : subValues.keySet()) {
System.out.println(values);
}
}
The output is:
{CDN={quality=200..300, cost=low, Video-type=mp4}}
{CDN={quality=200..300, cost=low, Video-type=mp4}}
{CDN={quality=200..300, cost=low, Video-type=mp4}}
First of all, I don't know why it repeats three times?
Secondly, I want to write a code that
if cost = low , then do somthing.
First of all, I dont know whay it reapets three times?
Because you tell it to. For each subValueKey, print the whole value set. There are three sub-keys, so the complete value set gets printed three times.
Secondly, I want to write a code that if cost = low , then do somthing.
Yaml yaml = new Yaml();
Map<String, Map<String, String>> values =
(Map<String, Map<String, String>>) yaml.load(
new FileInputStream(new File(
"/workspace/servlet-yaml/src/test.yaml")));
final Map<String, String> cdn = values.get("CDN");
// or iterate over all keys like you currently do
final String cost = cdn.get("cost");
// or iterate over all subkeys and compare them to "cost".
// that way, it's easier to handle missing keys.
if ("low".equals(cost)) {
// do something
}

How the value of HashMap changes?

I parse some xml files using XStream libraries. The result is a map for every file. When I debug, the result map is what I was looking for, but when I go to next line the value of map changes out of the blue! However it doesn't go for the next round in the "for" loop, it contains the information of next file. What can cause it?
public class Debug {
public String path = "E:\\Projects\\svn\\FANRPProduction\\WMS\\src\\main\\resources\\wms\\bpmn\\bp";
public XStream xStream;
public Map<String, List<CallActivity>> bpTpMap;
public void findBPandTP() throws IOException {
File root = new File(path);
File[] xmlFiles = FindAllXMLFiles.recursive(root);
bpTpMap=new HashMap<String, List<CallActivity>>();
for (File xml : xmlFiles) {
if (xml != null) {
xStream = new XStream(new StaxDriver());
xStream.alias("definitions", CallActivity.class);
xStream.registerConverter(new CallActivityConverter());
bpTpMap = (Map) xStream.fromXML(xml);//Here I get correct information. For example "WMS_RBP_OutgoingWeighing"
List<String> bpList = new ArrayList<String>(bpTpMap.keySet()); //Here I see the name of the next file in the path in bpTpMap, which is "WMS_BP_WeighingConfiguration"
}
}
}
}
I would suggest you to debug with the following:
bpTpMap = (Map) xStream.fromXML(xml);//Here I get correct information. For example "WMS_RBP_OutgoingWeighing"
System.out.println(bpTpMap.size());
Set<String> setOfKeys = bpTpMap.keySet();
System.out.println("Initial value of keys:"+bpTpMap);//Here you would see any extra values if the map has it

put/add is replacing all entires in a map or list

I am trying to convert a map: Map<String, Map<String, Map<String, Map<String, String>>>>to a Map<String, Settings>.
The Settings class contains all the possible map keys and will be set to true when looping through this particular key.
The problem is when in the deepest map, when adding to a global Map<String, Settings>, the Settings will be replaved with the last Settings for every entry.
Can someone help me find out where i do wrong?
public void loop(Map map, Settings settings){
List keys = new ArrayList(map.keySet());
if(map.get(keys.get(0)) instanceof Map){
//is a map, so continue loop + add to vorm
for(int i = 0; i < keys.size(); i++){
Settings tmp = settings;
String field = keys.get(i).toString();
Method method = null;
try {
//Set some booleans for key
method = tmp.getClass().getMethod(field, boolean.class);
method.invoke(tmp, true);
loop((Map) map.get(keys.get(i).toString()), tmp);
} catch (Exception e) {
e.printStackTrace();
}
}else{
for(int i = 0; i < keys.size(); i++) {
Settings tmp = settings;
String key = keys.get(i).toString();
String word = map.get(key).toString();
tmp.setWord(word);
Settings input = tmp;
settingsList.add(convert, input);//put into 2 arraylists
keyList.add(convert, woord);
convert++;
//vormen.put(word, tmp);//put into list
}
}
}
This method is called here:
public void convert(){
vormen = new HashMap();
settingsList = new ArrayList<>();
wordList = new ArrayList<>();
if(jsonMap.isEmpty()){
throw new NullPointerException("You are trying to convert a null map");
}else {
loop(jsonMap, new Settings());
}
}
Not every variable might be correctly named, i just renamed them.
Thanks for you help
EDIT: fixed, the temporary Settings that was put into the arrays was somehow being changed every time, acting like a pointer or something. I made a new Settings just before adding, and set the settings of tmp to that one. It works now.

How can I write Java properties in a defined order?

I'm using java.util.Properties's store(Writer, String) method to store the properties. In the resulting text file, the properties are stored in a haphazard order.
This is what I'm doing:
Properties properties = createProperties();
properties.store(new FileWriter(file), null);
How can I ensure the properties are written out in alphabetical order, or in the order the properties were added?
I'm hoping for a solution simpler than "manually create the properties file".
As per "The New Idiot's" suggestion, this stores in alphabetical key order.
Properties tmp = new Properties() {
#Override
public synchronized Enumeration<Object> keys() {
return Collections.enumeration(new TreeSet<Object>(super.keySet()));
}
};
tmp.putAll(properties);
tmp.store(new FileWriter(file), null);
See https://github.com/etiennestuder/java-ordered-properties for a complete implementation that allows to read/write properties files in a well-defined order.
OrderedProperties properties = new OrderedProperties();
properties.load(new FileInputStream(new File("~/some.properties")));
Steve McLeod's answer used to work for me, but since Java 11, it doesn't.
The problem seemed to be EntrySet ordering, so, here you go:
#SuppressWarnings("serial")
private static Properties newOrderedProperties()
{
return new Properties() {
#Override public synchronized Set<Map.Entry<Object, Object>> entrySet() {
return Collections.synchronizedSet(
super.entrySet()
.stream()
.sorted(Comparator.comparing(e -> e.getKey().toString()))
.collect(Collectors.toCollection(LinkedHashSet::new)));
}
};
}
I will warn that this is not fast by any means. It forces iteration over a LinkedHashSet which isn't ideal, but I'm open to suggestions.
To use a TreeSet is dangerous!
Because in the CASE_INSENSITIVE_ORDER the strings "mykey", "MyKey" and "MYKEY" will result in the same index! (so 2 keys will be omitted).
I use List instead, to be sure to keep all keys.
List<Object> list = new ArrayList<>( super.keySet());
Comparator<Object> comparator = Comparator.comparing( Object::toString, String.CASE_INSENSITIVE_ORDER );
Collections.sort( list, comparator );
return Collections.enumeration( list );
The solution from Steve McLeod did not not work when trying to sort case insensitive.
This is what I came up with
Properties newProperties = new Properties() {
private static final long serialVersionUID = 4112578634029874840L;
#Override
public synchronized Enumeration<Object> keys() {
Comparator<Object> byCaseInsensitiveString = Comparator.comparing(Object::toString,
String.CASE_INSENSITIVE_ORDER);
Supplier<TreeSet<Object>> supplier = () -> new TreeSet<>(byCaseInsensitiveString);
TreeSet<Object> sortedSet = super.keySet().stream()
.collect(Collectors.toCollection(supplier));
return Collections.enumeration(sortedSet);
}
};
// propertyMap is a simple LinkedHashMap<String,String>
newProperties.putAll(propertyMap);
File file = new File(filepath);
try (FileOutputStream fileOutputStream = new FileOutputStream(file, false)) {
newProperties.store(fileOutputStream, null);
}
I'm having the same itch, so I implemented a simple kludge subclass that allows you to explicitly pre-define the order name/values appear in one block and lexically order them in another block.
https://github.com/crums-io/io-util/blob/master/src/main/java/io/crums/util/TidyProperties.java
In any event, you need to override public Set<Map.Entry<Object, Object>> entrySet(), not public Enumeration<Object> keys(); the latter, as https://stackoverflow.com/users/704335/timmos points out, never hits on the store(..) method.
In case someone has to do this in kotlin:
class OrderedProperties: Properties() {
override val entries: MutableSet<MutableMap.MutableEntry<Any, Any>>
get(){
return Collections.synchronizedSet(
super.entries
.stream()
.sorted(Comparator.comparing { e -> e.key.toString() })
.collect(
Collectors.toCollection(
Supplier { LinkedHashSet() })
)
)
}
}
If your properties file is small, and you want a future-proof solution, then I suggest you to store the Properties object on a file and load the file back to a String (or store it to ByteArrayOutputStream and convert it to a String), split the string into lines, sort the lines, and write the lines to the destination file you want.
It's because the internal implementation of Properties class is always changing, and to achieve the sorting in store(), you need to override different methods of Properties class in different versions of Java (see How to sort Properties in java?). If your properties file is not large, then I prefer a future-proof solution over the best performance one.
For the correct way to split the string into lines, some reliable solutions are:
Files.lines()/Files.readAllLines(), if you use a File
BufferedReader.readLine() (Java 7 or earlier)
IOUtils.readLines(bufferedReader) (org.apache.commons.io.IOUtils, Java 7 or earlier)
BufferedReader.lines() (Java 8+) as mentioned in Split Java String by New Line
String.lines() (Java 11+) as mentioned in Split Java String by New Line.
And you don't need to be worried about values with multiple lines, because Properties.store() will escape the whole multi-line String into one line in the output file.
Sample codes for Java 8:
public static void test() {
......
String comments = "Your multiline comments, this should be line 1." +
"\n" +
"The sorting should not mess up the comment lines' ordering, this should be line 2 even if T is smaller than Y";
saveSortedPropertiesToFile(inputProperties, comments, Paths.get("C:\\dev\\sorted.properties"));
}
public static void saveSortedPropertiesToFile(Properties properties, String comments, Path destination) {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
// Storing it to output stream is the only way to make sure correct encoding is used.
properties.store(outputStream, comments);
/* The encoding here shouldn't matter, since you are not going to modify the contents,
and you are only going to split them to lines and reorder them.
And Properties.store(OutputStream, String) should have translated unicode characters into (backslash)uXXXX anyway.
*/
String propertiesContentUnsorted = outputStream.toString("UTF-8");
String propertiesContentSorted;
try (BufferedReader bufferedReader = new BufferedReader(new StringReader(propertiesContentUnsorted))) {
List<String> commentLines = new ArrayList<>();
List<String> contentLines = new ArrayList<>();
boolean commentSectionEnded = false;
for (Iterator<String> it = bufferedReader.lines().iterator(); it.hasNext(); ) {
String line = it.next();
if (!commentSectionEnded) {
if (line.startsWith("#")) {
commentLines.add(line);
} else {
contentLines.add(line);
commentSectionEnded = true;
}
} else {
contentLines.add(line);
}
}
// Sort on content lines only
propertiesContentSorted = Stream.concat(commentLines.stream(), contentLines.stream().sorted())
.collect(Collectors.joining(System.lineSeparator()));
}
// Just make sure you use the same encoding as above.
Files.write(destination, propertiesContentSorted.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
// Log it if necessary
}
}
Sample codes for Java 7:
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
......
public static void test() {
......
String comments = "Your multiline comments, this should be line 1." +
"\n" +
"The sorting should not mess up the comment lines' ordering, this should be line 2 even if T is smaller than Y";
saveSortedPropertiesToFile(inputProperties, comments, Paths.get("C:\\dev\\sorted.properties"));
}
public static void saveSortedPropertiesToFile(Properties properties, String comments, Path destination) {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
// Storing it to output stream is the only way to make sure correct encoding is used.
properties.store(outputStream, comments);
/* The encoding here shouldn't matter, since you are not going to modify the contents,
and you are only going to split them to lines and reorder them.
And Properties.store(OutputStream, String) should have translated unicode characters into (backslash)uXXXX anyway.
*/
String propertiesContentUnsorted = outputStream.toString("UTF-8");
String propertiesContentSorted;
try (BufferedReader bufferedReader = new BufferedReader(new StringReader(propertiesContentUnsorted))) {
List<String> commentLines = new ArrayList<>();
List<String> contentLines = new ArrayList<>();
boolean commentSectionEnded = false;
for (Iterator<String> it = IOUtils.readLines(bufferedReader).iterator(); it.hasNext(); ) {
String line = it.next();
if (!commentSectionEnded) {
if (line.startsWith("#")) {
commentLines.add(line);
} else {
contentLines.add(line);
commentSectionEnded = true;
}
} else {
contentLines.add(line);
}
}
// Sort on content lines only
Collections.sort(contentLines);
propertiesContentSorted = StringUtils.join(IterableUtils.chainedIterable(commentLines, contentLines).iterator(), System.lineSeparator());
}
// Just make sure you use the same encoding as above.
Files.write(destination, propertiesContentSorted.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
// Log it if necessary
}
}
True that keys() is not triggered so instead of passing trough a list as Timmos suggested you can do it like this:
Properties alphaproperties = new Properties() {
#Override
public Set<Map.Entry<Object, Object>> entrySet() {
Set<Map.Entry<Object, Object>> setnontrie = super.entrySet();
Set<Map.Entry<Object, Object>> unSetTrie = new ConcurrentSkipListSet<Map.Entry<Object, Object>>(new Comparator<Map.Entry<Object, Object>>() {
#Override
public int compare(Map.Entry<Object, Object> o1, Map.Entry<Object, Object> o2) {
return o1.getKey().toString().compareTo(o2.getKey().toString());
}
});
unSetTrie.addAll(setnontrie);
return unSetTrie;
}
};
alphaproperties.putAll(properties);
alphaproperties.store(fw, "UpdatedBy Me");
fw.close();

Categories

Resources