I'm working on converting a delimited file into an ordered tree structure. Below is a sample input...
1.2.3.4.5
1.3.2.4.5
1.2.4.5.6
I need to be able to convert this into an output like the following (in a searchable tree structure)...
1
-2
--3
---4
----5
--4
---5
----6
-3
--2
---4
----5
My thoughts on a solution to this was to...
Iterate the text file and create an arraylist that represents each row
Use Collections.sort() to make the arraylist sorted
Use a TreeMap to store the "base" record as a key (1 in this case) and an arrayList of strings to contain all the records
Iterate the TreeMap's keys and convert its arrayList to a LinkedHashSet that contains Nodes that represent each entry
Iterate the Tree's keys and print each node buy its index value
I thought everything was working well but when I started testing this approach, I found my output looks like below...
1
-2
--3
---4
----5
--4
---5
----6
-3
--2
As can be seen, the Nodes under 3/2/xx are not present, this is due to the logic I am using to build the LinkedHashSet for my Node values (Node(3, 4)) will simply be ignore because its a duplicate Node. I thought I was going in the right direction with this but now I can see my logic is clearly flawed. Does anyone have any suggestions for an approach for something like this? My current code is below...
TreeBuilder.java
public class TreeBuilder {
public static void main(String[] args) {
// Get a list of records
List<String> data = new ArrayList<String>();
data.add("1.2.3.4.5");
data.add("1.3.2.4.5");
data.add("1.2.4.5.6");
Collections.sort(data);
// Build the "Base" tree
TreeMap<String, List<String>> tree = buildBaseTree(data);
// Build the target tree structure
TreeMap<String, LinkedHashSet<Node>> finalTree = convertListToSet(tree);
printRecords(finalTree);
}
public static void printRecords(
TreeMap<String, LinkedHashSet<Node>> recordTree) {
System.out.println("---------Records---------");
for (Map.Entry<String, LinkedHashSet<Node>> entry : recordTree
.entrySet()) {
System.out.println(entry.getKey());
// Print out the structured data
StringBuilder stringBuilder = new StringBuilder();
Iterator<Node> iterator = entry.getValue().iterator();
while (iterator.hasNext()) {
Node node = iterator.next();
for (int i = 0; i < node.index; i++) {
stringBuilder.append("-");
}
System.out.println(stringBuilder.toString() + node.value);
// "reset" the builder
stringBuilder.setLength(0);
}
}
}
private static TreeMap<String, LinkedHashSet<Node>> convertListToSet(
TreeMap<String, List<String>> tree) {
TreeMap<String, LinkedHashSet<Node>> finalMap = new TreeMap<String, LinkedHashSet<Node>>();
LinkedHashSet<Node> linkedHashSet = new LinkedHashSet<Node>();
// Iterate the entry set
for (Map.Entry<String, List<String>> entry : tree.entrySet()) {
List<String> records = entry.getValue();
for (String record : records) {
String[] recordArray = record.split("\\.");
for (int i = 1; i < recordArray.length; i++) {
Node node = new Node(i, Integer.parseInt(recordArray[i]));
linkedHashSet.add(node);
}
}
finalMap.put(entry.getKey(), linkedHashSet);
// reset our linkedHashSet
linkedHashSet = new LinkedHashSet<Node>();
}
System.out.println("Final map " + finalMap);
return finalMap;
}
/**
* Builds a tree with base record keys and a list of records for each key.
*
* #param data
* #return
*/
private static TreeMap<String, List<String>> buildBaseTree(List<String> data) {
TreeMap<String, List<String>> tree = new TreeMap<String, List<String>>();
List<String> recordList = null;
// First find all base records
for (String record : data) {
String[] baseEntry = record.split("\\.");
if (!tree.containsKey(baseEntry[0])) {
recordList = new ArrayList<String>();
tree.put(baseEntry[0], recordList);
}
}
// Now place all sub-records in each base record
for (String record : data) {
String[] baseEntry = record.split("\\.");
tree.get(baseEntry[0]).add(record);
}
System.out.println("------------------Base Tree---------------");
System.out.println(tree);
System.out.println("------------------------------------------");
return tree;
}
private static List<String> readData(String file) {
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(new File(file)));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
List<String> data = new ArrayList<String>();
// Get a list of all the records
String line = null;
try {
while ((line = bufferedReader.readLine()) != null) {
data.add(line);
}
} catch (IOException e) {
e.printStackTrace();
}
// Sort the list so its ordered
System.out.println("-------------Sorted Data Set-----------");
Collections.sort(data);
for (String record : data) {
System.out.println(record);
}
System.out.println("---------------------------------------");
return data;
}
}
Node.java
public class Node implements Comparable<Node> {
int index;
int value;
public Node(int index, int value) {
this.index = index;
this.value = value;
}
public int getIndex() {
return index;
}
#Override
public String toString() {
return "Node [index=" + index + ", value=" + value + "]";
}
public void setIndex(int index) {
this.index = index;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
#Override
public int compareTo(Node o) {
Node otherNode = (Node) o;
if (this.index > otherNode.index)
return 1;
if (this.index < otherNode.index) {
return -1;
}
return 0;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + index;
result = prime * result + value;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Node other = (Node) obj;
if (index != other.index)
return false;
if (value != other.value)
return false;
return true;
}
}
It doesn't have to be complicated. All you need is a SortedMap of SortedMap instances and there's only one trick to it: Parameterizing it recursively for type safety (if desired).
package com.acme;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
List<String> rows = new ArrayList<>();
rows.add("1.2.3.4.5");
rows.add("1.3.2.4.5");
rows.add("1.2.4.5.6");
MyTreeMap root = new MyTreeMap();
for (String row : rows) {
MyTreeMap n = root;
String[] cells = row.split("\\.");
for (String cell : cells) {
MyTreeMap child = n.get(cell);
if (child == null) {
n.put(cell, child = new MyTreeMap());
}
n = child;
}
}
print(root, "", "-");
}
static void print(MyTreeMap m, String indentationStr, String indentationStrAddition) {
for (Entry<String, MyTreeMap> o : m.entrySet()) {
System.out.println(indentationStr + o.getKey());
print(o.getValue(), indentationStr + indentationStrAddition, indentationStrAddition);
}
}
/**
* This is just a construct that helps us to parameterize recursively.
*/
static class MyTreeMap extends TreeMap<String, MyTreeMap> {private static final long serialVersionUID = 1L;}
}
If I understand your problem you want to create search trees from a list of integers that share common prefixes. If that's the case then I think you can achieve this with a lot less code:
private static class TreeNode {
private final Map<Integer, TreeNode> children = new HashMap<>();
public void insert(List<Integer> values) {
if (!values.isEmpty()) {
children.putIfAbsent(values.get(0), new TreeNode());
children.get(values.get(0)).insert(values.subList(1, values.size()));
}
}
public void print(int level) {
for (Map.Entry<Integer, TreeNode> entry : children.entrySet()) {
System.out.print(String.join("", Collections.nCopies(level, "-")));
System.out.println(entry.getKey());
entry.getValue().print(level + 1);
}
}
}
I'm not certain if you intend to sort the list of integers. If so you can add a Collections.sort at the appropriate place in the code.
Related
I'm trying to implement a hashtable using chaining and without any libraries (other than those already in my code), but I'm very stuck. For some reason, the data (100 lines of ints) isn't being added to the list, as seen when it's printed, except for one in the second position (which I assume I need a toString() method for.) Is there any tips or solutions I could get as to how to make this work?
Thanks in advance!
Main + array declaration:
static LinkedList<Node> hashTable[] = new LinkedList[100];
static class Node {
int value;
int key;
}
public static void main(String[] args) throws FileNotFoundException {
File f = new File("Ex5.txt");
Scanner scan = new Scanner(f);
if (f.exists() == false) {
System.out.println("File doesn't exist or could not be found.");
System.exit(0);
}
while (scan.hasNextInt()) {
int n = scan.nextInt();
insert(1, hashFunction(n));
}
for (int i = 0; i < 100; ++i) {
System.out.println(hashTable[i]);
}
}
Insert Function:
public static void insert(int key, int value) {
int index = key % 100;
LinkedList<Node> items = hashTable[index];
if (items == null) {
items = new LinkedList<>();
Node item = new Node();
item.key = key;
item.value = value;
items.add(item);
hashTable[index] = items;
} else {
for (Node item : items) {
if (item.key == key) {
item.value = value;
return;
}
}
Node item = new Node();
item.key = key;
item.value = value;
items.add(item);
}
}
Hashing function:
public static int hashFunction(int value) {
int hashKey = value % 100;
return hashKey;
}
You shall make two changes:
You shall use hashfunction to get the key and keep value as is.
Remove index=key%100 from insert, instead use passed key for
traversing.
Hope this helps.
-------------------------- EDIT --------------------
For printing actual values in linked list, please override toString() method in your Node Class and return a string representation of Key-Value.
This question already has answers here:
How do I remove repeated elements from ArrayList?
(40 answers)
Closed 3 years ago.
List list = ["f1,f2","f2,f3","f4,f5","f2,f1","f5,f4"];
output list would be
List uniqueList = ["f1,f2","f2,f3","f4,f5"]
I have another solution . If you dont want to prepare another class to compare values inside List . You can separete each value by comma and sort those data. After that you can again converte them to Set of String
public static void main(String[] args) {
List<String> stringList = Arrays.asList("f1,f2", "f2,f3", "f4,f5", "f2,f1", "f5,f4");
Set<String> result = new HashSet<>();
for (String s : stringList) {
String[] elements = s.split(",");
Arrays.sort(elements);
result.add(Arrays.toString(elements));
}
for (String e : result){
System.out.println(e);
}
}
Using an additional class:
static class Pair {
String a, b;
Pair(String s) {
String[] arr = s.split(",");
this.a = arr[0];
this.b = arr[1];
}
static String pairToString(Pair p) {
return p.a + "," + p.b;
}
#Override
public int hashCode() {
return Objects.hash(a, b) + Objects.hash(b, a);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pair p = (Pair) o;
return (p.a.equals(a) && p.b.equals(b)) || (p.b.equals(a) && p.a.equals(b));
}
}
Now you can use:
public static void main(String[] args) {
List<String> list = Arrays.asList("f1,f2", "f2,f3", "f4,f5", "f2,f1", "f5,f4");
List<String> strings = list
.stream()
.map(Pair::new)
.distinct()
.map(Pair::pairToString)
.collect(Collectors.toList());
}
I have created a class to model the pairs and override the equals method to treat "f1,f2" and "f2,f1" as equals and then found out the duplicates using HashSet.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
public class so1 {
public static void main(String[] args) {
List<String> list = Arrays.asList(new String[] {"f1,f2","f2,f3","f4,f5","f2,f1","f5,f4"});
List<pair> plist = new ArrayList<pair>();
for (int i = 0; i < list.size(); i++) {
plist.add(new pair(list.get(i)));
}
HashSet<pair> hs = new HashSet<pair>();
for (int i = 0; i < plist.size(); i++) {
if(!hs.add(plist.get(i))){
System.out.println("Found duplicate "+plist.get(i).toString());
}
}
List<String> uniqueItems = new ArrayList<String>();
for (Iterator iterator = hs.iterator(); iterator.hasNext();) {
pair pair = (pair) iterator.next();
uniqueItems.add(pair.toString());
}
System.out.println(uniqueItems);
}
}
class pair{
pair(String inp){
String[] tokens = inp.split(",");
Arrays.sort(tokens);
for(String t: tokens){
elements.add(t);
}
}
List<String> elements = new ArrayList<>();
#Override
public String toString() {
return ""+elements;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((elements == null) ? 0 : elements.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
pair other = (pair) obj;
if (elements == null) {
if (other.elements != null)
return false;
} else if (!elements.equals(other.elements))
return false;
return true;
}
}
Here are a couple answers from https://www.geeksforgeeks.org/how-to-remove-duplicates-from-arraylist-in-java/
Java 7
// Create a new ArrayList
ArrayList<String> uniqueList = new ArrayList<String>();
// Traverse through the first list
for ( element : list) {
// If this element is not present in uniqueList
// then add it
if (!uniqueList.contains(element)) {
uniqueList.add(element);
}
}
Java 8
List<String> uniqueList = list.stream()
.distinct()
.collect(Collectors.toList());
I have a program that saves/loads data to a text file. However, some data seems to not be saving, and I can't figure out why.
Here's a simplified version of the code.
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
public class Test {
private static DumperOptions options;
private static File saveLocation;
private static Map<String, Object> data;
private static Representer representer;
private static String path;
private static Yaml yaml;
private static void setup() {
saveLocation = new File(path);
if (!saveLocation.exists())
try {
saveLocation.createNewFile();
} catch (IOException exception) {
exception.printStackTrace();
}
setupDumper();
yaml = new Yaml(representer, options);
data = Collections.synchronizedMap(new LinkedHashMap<String, Object>());
}
private static void setupDumper() {
options = new DumperOptions();
representer = new Representer();
options.setIndent(2);
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
options.setAllowUnicode(Charset.defaultCharset().name().contains("UTF"));
representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
}
public static boolean contains(String key) {
return data.containsKey(key);
}
public static boolean exists() {
return data != null;
}
public static boolean hasValue(String key) {
Object tempObject = data.get(key);
return (tempObject != null);
}
public static boolean isEmpty() {
return data.isEmpty() || data == null;
}
public static int getInteger(String key) {
Object tempObject = get(key);
if (tempObject instanceof Integer)
return (Integer) tempObject;
if (tempObject instanceof String)
return Integer.parseInt(tempObject.toString());
if (tempObject instanceof Number)
return Integer.parseInt(tempObject.toString());
return -1;
}
public static Object get(String key) {
if (isEmpty())
return null;
if (key.contains(".")) {
String[] nodes = key.split("\\.");
Map<String, Object> currParent;
if (data.containsKey(nodes[0]) && (data.get(nodes[0]) instanceof Map))
currParent = (Map) data.get(nodes[0]);
else return null;
if (nodes.length > 1) {
for (int i = 1; i < nodes.length - 1; i++) {
if (currParent.containsKey(nodes[i]) && (currParent.get(nodes[i]) instanceof Map))
currParent = (Map) currParent.get(nodes[i]);
else return null;
}
if (currParent.containsKey(nodes[nodes.length - 1]))
return currParent.get(nodes[nodes.length - 1]);
}
} else if (!contains(key) || (contains(key) && !hasValue(key)))
return null;
return data.get(key);
}
public static FileReader read(File file) {
try {
if (!file.exists())
return null;
return new FileReader(file);
} catch (FileNotFoundException exception) {
exception.printStackTrace();
}
return null;
}
public static boolean load() {
setup();
FileReader reader = read(saveLocation);
if (reader == null) {
data = Collections.synchronizedMap(new LinkedHashMap<String, Object>());
return true;
}
data = Collections.synchronizedMap((Map<String, Object>) yaml.load(reader));
System.out.println(getInteger("Settings.Difficulty.Level"));
System.out.println(getInteger("Settings.Difficulty.Locked"));
System.out.println(getInteger("Settings.Cheats.Enabled"));
System.out.println(getInteger("Settings.Cheats.Locked"));
System.out.println(getInteger("Settings.GUI.Enabled"));
System.out.println(data.toString());
return true;
}
public static void save() {
//Settings
set("Settings.Difficulty.Level", 1);// not saving
set("Settings.Difficulty.Locked", 2);
set("Settings.Cheats.Enabled", 3); // not saving
set("Settings.Cheats.Locked", 4);
set("Settings.GUI.Enabled", 5);
try {
if (!saveLocation.exists())
saveLocation.createNewFile();
FileWriter writer = new FileWriter(saveLocation);
writer.write(yaml.dump(data));
writer.flush();
writer.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
public static void set(String key, Object object) {
if (!exists())
return;
if (key.contains(".")) {
String[] nodes = key.split("\\.");
// if data doesn't contain top-level node, create nested Maps
if (!data.containsKey(nodes[0])) {
Map<String, Object> currNode = new HashMap<>(), prevNode;
currNode.put(nodes[nodes.length - 1], object);
for (int i = nodes.length - 2; i > 0; i--) {
prevNode = currNode;
currNode = new HashMap<>();
currNode.put(nodes[i], prevNode);
}
// set top-level node to previous parent
data.put(nodes[0], currNode);
} else { // if data contains top-level node, work through each Map
Map[] prevNodes = new LinkedHashMap[nodes.length - 1];
if (nodes.length > 1) {
for (int i = 0; i <= nodes.length - 2; i++) {
if (data.containsKey(nodes[i]) && (data.get(nodes[i]) instanceof Map))
prevNodes[i] = new LinkedHashMap((Map) data.get(nodes[i]));
else if (!data.containsKey(nodes[i]))
prevNodes[i] = new LinkedHashMap();
}
prevNodes[prevNodes.length - 1].put(nodes[nodes.length - 1], object);
for (int i = prevNodes.length - 1; i >= 1; i--)
prevNodes[i - 1].put(nodes[i], prevNodes[i]);
data.put(nodes[0], prevNodes[0]);
} else data.put(nodes[0], object);
}
return;
}
data.put(key, object);
}
public static void main(String[] args) {
path = "test.txt";
setup();
save();
load();
}
}
Normally it'll save integers from a bunch of other classes, then when the program is run again, loads it back into the appropriate variables. But for the sake of testing, all it does is print out the values.
//Settings
set("Settings.Difficulty.Level", 1);// not saving
set("Settings.Difficulty.Locked", 2);
set("Settings.Cheats.Enabled", 3); // not saving
set("Settings.Cheats.Locked", 4);
set("Settings.GUI.Enabled", 5);
This should save integers (1 to 5) to test.txt, and then prints them to the console
System.out.println(getInteger("Settings.Difficulty.Level"));
System.out.println(getInteger("Settings.Difficulty.Locked"));
System.out.println(getInteger("Settings.Cheats.Enabled"));
System.out.println(getInteger("Settings.Cheats.Locked"));
System.out.println(getInteger("Settings.GUI.Enabled"));
When I run the file, I expect to get the output of
1
2
3
4
5
But I actually get
-1
2
-1
4
5
Settings.Difficulty.Level and Settings.Cheats.Enabled doesnt even seem to be saved in the file at all.
Settings:
Difficulty:
Locked: 2
Cheats:
Locked: 4
GUI:
Enabled: 5
I'm pretty sure the issue is somewhere in the set method, but I'm not sure exactly what it is. I also don't know why its only two of the 5 variables that won't save (Must be something to do with the name used).
The error is here:
for (int i = 0; i <= nodes.length - 2; i++) {
if (data.containsKey(nodes[i]) && (data.get(nodes[i]) instanceof Map))
prevNodes[i] = new LinkedHashMap((Map) data.get(nodes[i]));
else if (!data.containsKey(nodes[i]))
prevNodes[i] = new LinkedHashMap();
}
This code is executed with the second set call. It finds "Settings" in data, but then, it does not find "Difficulty" in data (because that is in the "Settings" map). So it creates a new, empty LinkedHashMap. Then, this code happens:
prevNodes[prevNodes.length - 1].put(nodes[nodes.length - 1], object);
for (int i = prevNodes.length - 1; i >= 1; i--)
prevNodes[i - 1].put(nodes[i], prevNodes[i]);
It replaces the existing "Difficulty" node (which contains "Level") with the newly created one (which contains only "Locked"). Thus, "Level" is missing afterwards.
You can modify your for loop like this:
for (int i = 0; i <= nodes.length - 2; i++) {
final Map cur = (i == 0) ? data : prevNodes[i - 1];
if (cur.containsKey(nodes[i]) && (cur.get(nodes[i]) instanceof Map))
prevNodes[i] = new LinkedHashMap((Map) cur.get(nodes[i]));
else if (!cur.containsKey(nodes[i]))
prevNodes[i] = new LinkedHashMap();
}
But actually, the whole set method can be simplified: (Beware, this is untested code and may contain minor errors)
public static void set(String key, Object object) {
if (!exists())
return;
// no need to differentiate between paths with and without "."
final String[] nodes = key.split("\\.");
Map cur = data;
for (int i = 0; i <= nodes.length - 2; ++i) {
Object val = cur.get(nodes[i]);
if (val == null) {
val = new LinkedHashMap();
cur.put(nodes[i], val);
} else if (!(val instanceof Map)) {
// error in structure, handle it here (throw exception, …)
}
cur = (Map) val;
}
cur.put(nodes[nodes.length - 1], object);
}
My objective is to create a class to represent the hierarchical pattern of category in java. Now from database i get the path i need to construct like below;
"123_456_789"
"123_67"
"895_444"
the above mentioned 2 strings are obtained as input. My objective is to convert the above data in to below structure
categories[
{
categoryId:123,
subcategories[{
categoryId:456,
subcategories[{
categoryId:789
}]
},
{
categoryId:67
}
]
},
{
categoryId:895,
subcategories[{
categoryId:444
}]
}]
What is the easiest way to convert the input to a java object like the class with list of categoryIds.
You firstly need to store it in a tree type data structure and then print as like how json pretty print does.
You need gson jar in classpath and you may find it at: gson.jar
Here is the working code for your asked question:
import java.util.*;
import java.util.Map.Entry;
import com.google.gson.*;
public class MainTest {
public static void main(String[] args) {
String arr[] = new String[] { "123_456_789", "123_67", "895_444" };
Node node = new Node(null, arr);
// System.out.println(node);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
JsonParser jp = new JsonParser();
JsonElement je = jp.parse(node.toString());
String prettyJsonString = gson.toJson(je);
System.out.println(prettyJsonString.replaceAll("\"", ""));
}
static class Node {
String value;
List<Node> list;
public Node(String value, String arr[]) {
this.value = value;
list = new ArrayList<Node>();
if (arr != null && arr.length > 0) {
Map<String, List<String>> map = new LinkedHashMap<String, List<String>>();
for (String str : arr) {
int _ = str.indexOf("_");
if (_ > 0) {
String key = str.substring(0, _);
String currentValue = str.substring(_ + 1);
List<String> list = map.get(key);
if (list == null) {
list = new ArrayList<String>();
}
list.add(currentValue);
map.put(key, list);
} else {
map.put(str, null);
}
}
for (Entry<String, List<String>> entry : map.entrySet()) {
if (entry.getValue() == null) {
list.add(new Node(entry.getKey(), null));
} else {
list.add(new Node(entry.getKey(), entry.getValue()
.toArray(new String[entry.getValue().size()])));
}
}
}
}
#Override
public String toString() {
if (this.value == null) {
StringBuilder sb = new StringBuilder();
sb.append("{\"categories\":[" + "\n");
Iterator<Node> iter = list.iterator();
while (iter.hasNext()) {
sb.append(iter.next());
if (iter.hasNext()) {
sb.append(",");
}
}
sb.append("]}");
return sb.toString();
} else {
StringBuilder sb = new StringBuilder();
Iterator<Node> iter = list.iterator();
sb.append("{\"categoryId\":" + value + "}"
+ (iter.hasNext() ? "," : "") + "\n");
if (iter.hasNext()) {
sb.append("{\"subcategories\":[" + "\n");
while (iter.hasNext()) {
// sb.append("{");
sb.append(iter.next());
// sb.append("}");
if (iter.hasNext()) {
sb.append(",");
}
}
sb.append("]}" + "\n");
}
return sb.toString();
}
}
}
}
I am new to Java and am using the TreeMap Code example that was on this site but when I try to iterate through the TreeMap, I get a list of null values but when I print the map directly I can see the key/value pairs. How do I rectify this?
import java.util.*;
public class Testing {
public static void main(String[] args) {
HashMap<String,Double> map = new HashMap<String,Double>();
ValueComparator1 bvc = new ValueComparator1(map);
TreeMap<String,Double> sorted_map = new TreeMap<String,Double>(bvc);
map.put("A",99.5);
map.put("B",67.4);
map.put("C",67.4);
map.put("D",67.3);
System.out.println("unsorted map: "+map);
sorted_map.putAll(map);
System.out.println("results: "+sorted_map);
for(String key: sorted_map.keySet())
{
System.out.println(sorted_map.get(key)); //null values-Why?
}
}
}
class ValueComparator1 implements Comparator<String> {
Map<String, Double> base;
public ValueComparator1(Map<String, Double> base) {
this.base = base;
}
// Note: this comparator imposes orderings that are inconsistent with equals.
public int compare(String a, String b) {
if (base.get(a) >= base.get(b)) {
return -1;
} else {
return 1;
} // returning 0 would merge keys
}
}
It doesn't work because your comparator doesn't return 0 when given identical keys, e.g. compare("A", "A"). Change it to
public int compare(String a, String b) {
Double va = base.get(a);
Double vb = base.get(b);
if(va > vb) {
return -1;
} else if(va < vb) {
return 1;
} else {
return a.compareTo(b);
}
}
and it'll work.
Sorry, but your example is a bit upside down. You're putting keys into a sorted map (tree map) but then using the values as keys in the sense that you compare by the values. It looks like you're looking to handle objects that have a key and a value, so here's something you might want to consider. This would certainly be the OOP way to handle "compound" concepts like the one you're modeling with a map.
class Pair implements Comparable<Pair> {
String value;
double key;
Pair(String value, double key) {
this.value = value;
this.key = key;
}
public int compareTo(Pair p) {
return Double.compare(key, p.key);
}
public String toString(Pair p) {
return value + "," + key;
}
}
static void main(String[] args) {
Set<Pair> unsortedSet = new HashSet<Pair>();
unsortedSet.add(new Pair("A", 99.5));
unsortedSet.add(new Pair("B", 67.4));
unsortedSet.add(new Pair("C", 67.4));
unsortedSet.add(new Pair("D", 67.3));
Set<Pair> sortedSet = new TreeSet<Pair>();
sortedSet.add(new Pair("A", 99.5));
sortedSet.add(new Pair("B", 67.4));
sortedSet.add(new Pair("C", 67.4));
sortedSet.add(new Pair("D", 67.3));
System.out.println("Unsorted set: " + unsortedSet);
System.out.println("Sorted set: " + sortedSet);
for (Pair pair : sortedSet) {
System.out.println(pair);
}
}
Since your comparator never returns 0 TreeMap.get() does not work. Still you can iterate over TreeMap entries like this
for (Entry<String, Double> e : sorted_map.entrySet()) {
System.out.println(e);
}
prints
A=99.5
C=67.4
B=67.4
D=67.3
The code of Adam Crume is very important. To explain you more on this, when you call your sorted_map.get(key), it goes to java.util.TreeMap class', getEntryUsingComparator method because you have set the comparator explicitly. This method looks like
final Entry<K,V> getEntryUsingComparator(Object key) {
K k = (K) key;
Comparator<? super K> cpr = comparator;
if (cpr != null) {
Entry<K,V> p = root;
while (p != null) {
int cmp = cpr.compare(k, p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
}
return null;
}
Since there is comparison for keys in your compare method of your value comparator, the entry will be null and hence value will also be null because map.get is implemented as
public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}