I have a function to traverse a complex deep HashMap structure. My problem is that when I find the desired node, and do any action on it, such as deleting it, I'm not actually doing any action on the data structure, but instead I'm operating on a copy of the data structure. I'd think a pointer such as in C++ would solve my problem, so how can I do this in Java?
The code:
private HashMap parentNode = null;
// a complex JSON string of arrays / objects, won't list for brevity
private String jsonString = ...
// parses JSON string into HashMaps for objects and Object[ ]s for arrays
private HashMap arr = (HashMap)JSON.parse(jsonString);
// find the node with an id of 27
HashMap parent = findNode(arr, "27");
// Print arr before modifying node
System.out.println(arr);
// modify parent in some way
parent = null;
// Print arr after modifying node
System.out.println(arr);
public HashMap findNode(HashMap map, String id) {
parentNode = null;
findNodeRecursive(map, id);
return parentNode;
}
public void findNodeRecursive(HashMap map, String id) {
for(Object entry : map.entrySet()){
Object value = ((Map.Entry)entry).getValue();
if((value instanceof String) && ((String)value).equals(id))
parentNode = map;
else if(value instanceof HashMap)
findNodeRecursive((HashMap)value,id);
else if(value instanceof Object[])
for(int i=0; i<((Object[])value).length; i++)
findNodeRecursive( (HashMap)(((Object[])value)[i]) ,id);
}
}
To delete the node you want (parent), change your
parent = null;
to
arr.remove(parent);
Setting it to null does not delete anything, simply changes the reference that once was pointing to the node, back to null. To delete, you need to do it explicitly by using the HashMap.remove() method
Related
I'm trying to create a hierarchy from flat data. I have the following Node definition:
public class Node {
public String name;
public List<Node> children = new ArrayList<>();
}
Given this data: [Person,Manager,Hourly,New], where the tree should be like:
Person
|--Manager
|--Hourly
|--New
I've tried the following:
public void run()
{
List<List<String>> objects = new ArrayList<>();
String[] str = {"Person","Manager","Hourly","New"};
objects.add(Arrays.asList(str)) ;
String[] str2 = {"Person","Manager","Salary"};
objects.add(Arrays.asList(str2)) ;
String[] str3 = {"Person","Manager","Salary", "New"};
objects.add(Arrays.asList(str3)) ;
// Create a Node with the sequence
myNode = new Node();
createNode(objects.get(0), 0, myNode, myNode);
LOG.debug(myNode.name);
}
And my createNode method is:
public Node createNode(List<String> seq, Integer start, Node parentNode, Node childNode)
{
// do something and return a Node?
}
But conceptually I don't understand how to maintain the structure if Java is return-by-value. What do I add to createNode so that I can add a Manager->Hourly->New hierarchy as a child to Person
You don't need both a Node return type and a Node argument to your method.
Here's one way to do it:
//in run()
myNode = new Node();
myNode.name = "Root";
createNode(objects.get(0), 0, myNode, myNode);
public void createNode(List<String> seq, Integer start, Node parentNode)
{
Node childNode = new Node();
childNode.name = seq[start];
parentNode.children.Add(childNode);
createNode(seq, start+1, childNode);
}
You don't need to return anything from createNode() -- since you have parentNode as a variable, you can add things to its children member. A call to createNode() will recursively add child nodes, following your string array to its end.
Another way to do it is like this:
public Node createNode(List<String> seq, Integer start)
{
if (start >= seq.Length) {
return null;
}
Node node = new Node();
node.name = seq[start];
node.children.Add(createNode(seq, start+1);
return node;
}
In this case, you don't need to pass in node references at all; calling createNode() will generate a new node object, fill its children tree recursively, and return the newly-generated node structure.
As I can see your defination of node is somewhat similar to adjacency list in graph.
In the Target node add the associated node in the list associated with the target node. This is true for each node belonging to all the nodes.
For each object belonging to the objects array (array parameter) in your createNode method, you need to create the Node object.
just pass an String array and the taeget node. Iterate the list and create a node. Add the node in the list.
To avoid duplicates while creating Node add them in an map. Key to the map should be String and value should be Node object. Before creating the object of node just try to get the object from the map, make the object only iff the object is not found in the map(in such a case create and add it to the map). In case object is found un the map, us the samedo not recreate it.
I am attempting to merge two API results which both return JSON into a single list of objects.
Currently, I'm basically iterating through two for loops, to match up the entries in each list. Is there a more efficient way of doing this? The process is currently taking much too long for my liking.
I have the following code:
private List<SingularEntry> combineAdsAndCreatives(JsonNode creativeEntries, JsonNode adEntries)
{
List<SingularEntry> entries = new ArrayList<SingularEntry>();
for (JsonNode node : adEntries) {
node = adHashToNode(node, creativeEntries);
if(node != null)
{
entries.add(convertNode(node));
}
}
return entries;
}
private JsonNode adHashToNode(JsonNode node, JsonNode creativeEntries)
{
for(JsonNode creativeNode : creativeEntries)
{
if( node.get("campaign_network_id").toString().equals(creativeNode.get("campaign_network_id").toString()) && node.get("ad_network").toString().equals(creativeNode.get("ad_network").toString()) )
{
ObjectNode newNode = (ObjectNode) node;
newNode.put("image_hash", creativeNode.get("image_hash"));
newNode.put("creative_name", creativeNode.get("creative_name"));
return newNode;
}
}
return null;
}
It's better to define a JsonNodeWrapper class which keeps the JsonNode and define equals() method (also you may need hashCode() method) to compare the wrapper with another JsonNode wrapper.
The go through both lists creating the JsonNodeWrappers and place all the created ones into a Set to leave unique ones only.
I am kind of new to java, I have this code
...
NodeList indexEntryList = sourceDoc.getElementsByTagName("in.ar"); //this is a xml tag
for (int i = 0; i < indexEntryList.getLength(); i++) {
...
}
...
I have to create a HashMap (or something like this), where I save a String which is an attribute of the node, and a List of all nodes that have the same attribute.
I think something like this:
Map<String, ArrayList<Node>> indexCategories = new HashMap<String, ArrayList<Node>>();
But in each time of the for, I dont know how to search in all keys of the Map, and add the new Node to the list, and if the key does no exists yet, create the new item inside the Map.
Use Map#containsKey() for searching if a key is present, Map#get() for obtaining the collection (if present) respective Map#put() for storing the newly created map. Everything could be found properly documented in the Map API, by the way.
If you look at NodeList's documentation, you'll see that it has only two methods: getLength() (which you've used) and Node item(int index).
So your loop will be:
for (int i = 0; i < indexEntryList.getLength(); i++) {
Node node = indexEntryList.item(i);
// do something with node
}
... and what you want to do with node, is find its attributes.
NamedNodeMap attributes = node.getAttributes();
if(attributes != null) {
for(int j=0;j < attributes.getLength(); j++) {
Node attribute = attributes.item(j);
// do something with node and attribute
}
}
... so what do you want to do with your attribute and its node? I'm not sure, but I think your intent is that map.get(attributeName) returns a list of nodes containing that element.
If so:
// get the list for this element, or create one
List list = map.get(attribute.getName());
if(list == null) {
list = new ArrayList();
map.put(attribute.getName(), list);
}
// add the node we're working with to that list
list.add(node);
A few notes here:
you might be better off with a Set than a List, because you could end up adding the same node to a list many times.
I really recommend putting each of these blocks into a separate method, calling one from the other -- that is, where I've put do something..., have a method call. That gives you smaller chunks of code that are easier to understand and easier to test; it would also mean that you could call the loop counter 'i' in both 'for' loops.
Try this one...
Map<String, ArrayList<Node>> indexCategories = new HashMap<String, ArrayList<Node>>();
public void addNode(string key, Node node) {
List<Node> nodes;
if(indexCategories.containsKey(key)) {
nodes = indexCategories.get(key);
}
else {
nodes = new ArrayList<Node>();
}
nodes.add(node);
indexCategories.put(key, node);
}
You can use something like:
String key = nodeAttribute;
if (!indexCategories.containsKey(key)) {
indexCategories.put(key, new ArrayList<Node>());
}
indexCategories.get(key).add(node);
YOu might try something like this
NodeList indexEntryList = sourceDoc.getElementsByTagName("in.ar"); //this is a xml tag
for (int i = 0; i < indexEntryList.getLength(); i++) {
Node oneItem = indexEntryList.item(i);
String someString = "xxx or however you obtain the string";
ArrayList<Node> listOfNodes = indexCategories.get(someString);
if (listOfNodes == null) {
listOfNodes = new ArrayList<Node>();
}
listOfNodes.add(oneItem);
}
I have a list named ionList which has values named structs in it.
Each struct in the list has more lists and those lists has some more structs in it. You can imagine it like kind of a parent list and child lists
I want to retrieve all the structs in the ionList completely and store it in one list named StructList.
The code i used is a recursive code but the data is getting lost when i call the recursive function whenevr i encounter a child list in my ionList.
public List<IonStruct> findStructsFromParentList(IonList ionList) {
String objectType = "object_type";
String diffType = "diff_type";
String childList = "children";
IonList ionChildList = null;
IonList ionChildList1 = null;
List<IonStruct> ionStructList = new ArrayList<IonStruct>();
System.out.println("ION LIST"+ionList.size());
for (int index = 0; index < ionList.size();index++) {
ionStructList.add((IonStruct) ionList.get(index));
ionChildList = (IonList) ionStructList.get(index).get(childList);
if (ionChildList != null) {
System.out.println("Found child");
ionStructList.addAll(findStructsFromParentList(ionChildList));
}
}
return ionStructList;
}
The childList found is altering my for loop initialisation in my recursive call which i think is the reason for not parsing more than one struct with child lists in it. Please help me out..
The line that says
ionChildList = (IonList) ionStructList.get(index).get(childList);
should say
ionChildList = (IonList)((IonStruct) ionList.get(index)).get(childList);
because within ionStructList, the indexes will be different, so with your code, you won't retrieve the right list.
I want to be able to access a certain node in my Doubly Linked List in O(1) time. I know that if i traverse the list to find a certain node it would take O(n) time so I want to map the nodes to an array list where I can access the nodes in O(1) time.
I'm really unsure how I would do this mapping. I would like to see an example of how this can be done.
Edit:
I would like to be able to access any node in the linked list so I can move the nodes around in O(1) time.
Example: Move node with ID 5 to end of list in O(1) time.
Edit 2: I uploaded a picture example of what I'm trying to accomplish
You can't do this with the built-in data structures ArrayList and LinkedList.
In general, it is not possible at all to have both of
O(1) indexing (by position in the list)
O(1) removing/adding/moving anywhere in the list.
The possibilities:
You can get to O(log(N)) for both these if you use a tree-based structure.
You can get to O(1) for indexing with a array-based structure, but then removing/adding in the middle takes O(n).
You can use a Hash-Map like-structure with adding/removing in O(1), but it only allows O(1) access by key, not access by index (other than iterating, i.e. O(n)). (This means, if you add/remove something in the middle, the indexes after it won't change.)
Even if you try to combine a linked list with an array, you'll have O(n) for removing/adding (since you still have to update the array).
Okay, with your added image to show what you want, it is doable. You are in fact reimplementing something like LinkedHashMap, but only with consecutive integer keys and with ability to manipulate the "Linked" part.
If your linked list consists of Node objects, you would have an ArrayList<Node>.
You would only add elements to the ArrayList when adding new nodes to the linked list, else use the ArrayList only for lookup.
Here is an example:
class FastIndexLinkedIntList<X> {
class Node {
Node next;
Node prev;
int key;
X value;
Node(int key, X val) { this.key = key; this.value = val; }
}
ArrayList<Node> indexedNodes = new ArrayList<Node>();
Node head;
Node tail;
public void add(X value) {
int key = indexedNodes.size();
Node node = new Node(key, value);
insertAtEnd(node);
indexedNodes.add(node);
}
private void insertAtEnd(Node n) {
if(tail == null) {
assert head == null;
head = n;
tail = n;
return;
}
n.prev = tail;
n.next = null;
tail.next = n;
tail = n;
}
private void removeNode(Node n) {
if(n.next == null) {
assert n == tail; // last node
tail = n.prev;
tail.next = null;
}
else {
n.next.prev = n.prev;
}
if(n.prev == null) {
assert n == head; // first node
head = n.next;
head.prev = null;
}
else {
n.prev.next = n.next;
}
}
public void moveNodeToEnd(int key) {
Node n = indexedNodes.get(key);
removeNode(n);
insertAtEnd(n);
}
}
You might want to add more operations here, but these are enough for the example in the question:
FastIndexedLinkedList<String> ll = new FastIndexedLinkedList<String>();
ll.add("0");
ll.add("1");
ll.add("2");
ll.add("3");
ll.moveNodeToEnd(2);
I'm not entirely sure of your purpose, do you simply wish to retrieve the object's index in O(1)?
This is how it would look:
LinkedList<Object> aList; // your LinkedList
Map<Object, Integer> mapper = new HashMap<Object, Integer>();
Object[] arr = aList.toArray();
for( int i = 0; i < arr.length; i++ ){
mapper.put( arr[i], i );
}
Now, if you want to find an object in your list, what you do is get its index from the mapper object with
mapper.get( o );
================================
Re: your edit
You can't (or there's none that I am aware of). You are essentially demanding the best of both worlds (linked lists and arrays).
LinkedHashMap: provides O(1) time and keys are doubly-linked list ordered.
Use the toArray() method to convert your LinkedList to an array for constant-time retrieval:
LinkedList.toArray(arr)