The following codes are aimed to export tree data structure into Map<String, String> so that can be easier to manipulate later. But the funny thing is toString() method works flawlessly but toMap() method got one missing parent A, child B. Anyone has any idea?
public static void main(String[] args) {
MutableTree<String> tree = new MappedTreeStructure<String>();
tree.add("A", "B");
tree.add("A", "C");
tree.add("C", "D");
tree.add("E", "F");
System.out.println(tree);
Map<String, String> myMap = tree.toMap();
if (myMap != null) {
for (Map.Entry<String, String> entry : myMap.entrySet()) {
System.out.println("parent: " + entry.getKey() + ", child: "
+ entry.getValue());
}
}
}
private final Map<N, N> nodeParent = new HashMap<N, N>();
private final LinkedHashSet<N> nodeList = new LinkedHashSet<N>();
#Override
public boolean add(N parent, N node) {
boolean added = nodeList.add(node);
nodeList.add(parent);
if (added) {
nodeParent.put(node, parent);
}
return added;
}
#Override
public boolean remove(N node, boolean cascade) {
if (!nodeList.contains(node)) {
return false;
}
if (cascade) {
for (N child : getChildren(node)) {
remove(child, true);
}
} else {
for (N child : getChildren(node)) {
nodeParent.remove(child);
}
}
nodeList.remove(node);
return true;
}
#Override
public List<N> getRoots() {
return getChildren(null);
}
#Override
public N getParent(N node) {
return nodeParent.get(node);
}
#Override
public List<N> getChildren(N node) {
List<N> children = new LinkedList<N>();
for (N n : nodeList) {
N parent = nodeParent.get(n);
if (node == null && parent == null) {
children.add(n);
} else if (node != null && parent != null && parent.equals(node)) {
children.add(n);
}
}
return children;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
dumpNodeStructure(builder, null, "- ");
return builder.toString();
}
#Override
public Map<String, String> toMap() {
Map<String, String> map = new HashMap<String, String>();
dumpNodeToMap(map, null);
return map;
}
private void dumpNodeToMap(Map<String, String> map, N node) {
if (node != null) {
map.put((String) getParent(node), node.toString());
}
for (N child : getChildren(node)) {
dumpNodeToMap(map, child);
}
}
private void dumpNodeStructure(StringBuilder builder, N node, String prefix) {
if (node != null) {
builder.append(prefix);
builder.append(node.toString());
builder.append('\n');
prefix = " " + prefix;
}
for (N child : getChildren(node)) {
dumpNodeStructure(builder, child, prefix);
}
}
The output are following on console:
- A
- B
- C
- D
- E
- F
parent: null, child: E
parent: A, child: C
parent: C, child: D
parent: E, child: F
For references, these are the two interface classes being used:
public interface MutableTree <N extends Serializable> extends Tree<N> {
public boolean add (N parent, N node);
public boolean remove (N node, boolean cascade);
Map<String, String> toMap();
}
and
public interface Tree <N extends Serializable> extends Serializable {
public List<N> getRoots ();
public N getParent (N node);
public List<N> getChildren (N node);
}
Your toMap method returns a a Map<String, String> whose key is the parent element name and whose value is a single child element. This means that when the key is "A" only one child element can be stored, and this is being set to the last child element which is found, in this case "C", overwriting the entry for "A" which was pointing to "B".
Instead your toMap method needs to return a Map<String, List<String>> which maps from each parent node, such as "A", to a List of child elements, such as "B" and "C". Obviously it's fine if the List contains only one child element, but it must be a list in case there is more than one child.
The usual pattern for creating a List of items rather than a single item looks like this:
String parentNode = getParent(node).toString();
List<String> childElements = null;
if(map.contains(parentNode) {
// List of child elements already exists, so get it from the Map.
childElements = map.get(parentNode);
} else {
// List of child elements does not yet exist, so create a new List
// and add it to the Map.
childElements = new ArrayList<>();
map.put(parentNode, childElements);
}
childElements.add(node.toString());
Related
I have a class that do a tree from directory, subdirectory and files. And need to satisfy two conditions. Sort the contents in following way:
-Directories should go first.
-Directories and files are sorted in lexicographic order (case-insensitive).
I work on windows system and code below work fine, all is sorted how I want. Directories go first and are sorted in lexicographic order. But I read that on windows system is automatically sorted in lexicographic order. But this code doesn't work on Linux, the line children.sort(Comparator.comparing(file -> file.isDirectory() ? -1 : 1)); do not work and files\directory aren't sorted in lexicographic order (case-insensitive). How to solve problem with sort conditions on linux system? How can change sort conditions in code?
import static java.util.Comparator.comparing;
public class TreeNode<T> implements Iterable<TreeNode<T>> {
public T data;
public TreeNode<T> parent;
public List<TreeNode<T>> children;
public boolean isRoot() {
return parent == null;
}
private List<TreeNode<T>> elementsIndex;
public TreeNode(T data) {
this.data = data;
this.children = new LinkedList<TreeNode<T>>();
this.elementsIndex = new LinkedList<TreeNode<T>>();
this.elementsIndex.add(this);
}
public TreeNode<T> addChild(T child) {
TreeNode<T> childNode = new TreeNode<T>(child);
childNode.parent = this;
this.children.add(childNode);
this.registerChildForSearch(childNode);
return childNode;
}
private void registerChildForSearch(TreeNode<T> node) {
elementsIndex.add(node);
if (parent != null)
parent.registerChildForSearch(node);
}
#Override
public String toString() {
return data != null ? data.toString() : "[data null]";
}
#Override
public Iterator<TreeNode<T>> iterator() {
TreeNode<T> iter = new TreeNode<T>((T) this);
return (Iterator<TreeNode<T>>) iter;
}
public static TreeNode<File> createDirTree(File folder) {
if (!folder.isDirectory()) {
throw new IllegalArgumentException("folder is not a Directory");
}
List<File> children = Arrays.asList(folder.listFiles());
children.sort(Comparator.comparing(file -> file.isDirectory() ? -1 : 1));
TreeNode<File> DirRoot = new TreeNode<File>(folder);
for (File file : children) {
if (file.isDirectory()) {
appendDirTree(file, DirRoot);
} else {
appendFile(file, DirRoot);
}
}
return DirRoot;
}
public static void appendDirTree(File folder, TreeNode<File> dirRoot) {
dirRoot.addChild(folder);
List<File> children = Arrays.asList(folder.listFiles());
children.sort(comparing(file -> file.isDirectory() ? -1 : 1));
for (File file : children) {
if (file.isDirectory()) {
appendDirTree(file, dirRoot.children.get(dirRoot.children.size() - 1));
} else {
appendFile(file, dirRoot.children.get(dirRoot.children.size() - 1));
}
}
}
public static void appendFile(File file, TreeNode<File> filenode) {
filenode.addChild(file);
}
public static String renderDirectoryTree(TreeNode<File> tree) {
List<StringBuilder> lines = renderDirectoryTreeLines(tree);
String newline = "\n";
StringBuilder sb = new StringBuilder(lines.size() * 20);
for (StringBuilder line : lines) {
sb.append(line);
sb.append(newline);
}
//System.out.println(sb);
return sb.toString();
}
public static List<StringBuilder> renderDirectoryTreeLines(TreeNode<File> tree) {
List<StringBuilder> result = new LinkedList<>();
result.add(new StringBuilder().append(tree.data.getName() + " " + calculateFileSize(tree) + " bytes"));
Iterator<TreeNode<File>> iterator = tree.children.iterator();
while (iterator.hasNext()) {
List<StringBuilder> subtree = renderDirectoryTreeLines(iterator.next());
if (iterator.hasNext()) {
addSubtree(result, subtree);
} else {
addLastSubtree(result, subtree);
}
}
return result;
}
private static void addSubtree(List<StringBuilder> result, List<StringBuilder> subtree) {
Iterator<StringBuilder> iterator = subtree.iterator();
result.add(iterator.next().insert(0, "├─ "));
while (iterator.hasNext()) {
result.add(iterator.next().insert(0, "│ "));
}
}
private static void addLastSubtree(List<StringBuilder> result, List<StringBuilder> subtree) {
Iterator<StringBuilder> iterator = subtree.iterator();
result.add(iterator.next().insert(0, "└─ "));
while (iterator.hasNext()) {
result.add(iterator.next().insert(0, " "));
}
}
public static long calculateFileSize(TreeNode<File> tree) {
long fileSize = 0;
if (tree.data.isDirectory()) {
List<TreeNode<File>> children = tree.children;
for (TreeNode<File> child : children) {
fileSize += calculateFileSize(child);
}
} else {
fileSize = tree.data.length();
}
return fileSize;
}
}
A short version could look something like this.
Comparator<File> lexicographicFileComparator = Comparator.comparing(File::isFile)
.thenComparing(Comparator.naturalOrder());
You've wrote that File.compareTo doesn't even check whether the file exists or not. I don't think it's the job of compareTo or Comparator to check if a file exists. Files that don't exist should be filtered before.
I think your example is overusing static methods. TreeNode should be an abstract class, while you have an implementation called FileTreeNode that replaces your static methods.
Here is an example
TreeNode
public abstract class TreeNode<N extends TreeNode<N, T>, T> implements Iterable<N> {
private static <N extends TreeNode<N, ?>> Stream<CharSequence> renderBranch(N node) {
Stream.Builder<CharSequence> result = Stream.builder();
result.add(node.printNode());
Iterator<N> iterator = node.getChildren().iterator();
while (iterator.hasNext()) {
Stream<CharSequence> subtree = renderBranch(iterator.next());
Iterator<CharSequence> subtreeIterator = subtree.iterator();
String branchSplit = "├─ ";
String branchSpacer = "│ ";
if (!iterator.hasNext()) {
branchSplit = "└─ ";
branchSpacer = " ";
}
result.add(branchSplit + subtreeIterator.next());
while (subtreeIterator.hasNext()) {
result.add(branchSpacer + subtreeIterator.next());
}
}
return result.build();
}
private final T data;
private final N parent;
private final List<N> children;
public TreeNode(T data) {
this(data, null);
}
protected TreeNode(T data, N parent) {
this.data = data;
this.parent = parent;
children = initChildren();
}
/**
* Called in constructor to initialize the children list.
*/
protected abstract List<N> initChildren();
/**
* Used to avoid unsafe casting.
*
* #return This
*/
protected abstract N instance();
/**
* TreeNode knows how to print the tree, but not how to print the current element. This way the child class can decide how it wants to be printed in the tree;
*
* #return readable text representation of node.
* #see TreeNode#renderBranch()
*/
protected abstract CharSequence printNode();
/**
* #return Returns a string representation of the entire branch starting at this node.
*/
public String renderBranch() {
Stream<CharSequence> lines = renderBranch(instance());
return lines.collect(Collectors.joining("\n"));
}
/**
* #return Returns a stream of the entire branch starting at this node
*/
public Stream<N> streamBranch() {
return Stream.concat(Stream.of(instance()), getChildren().stream().flatMap(TreeNode::streamBranch));
}
public T getData() {
return data;
}
public N getParent() {
return parent;
}
public List<N> getChildren() {
return children;
}
public boolean isRoot() {
return parent == null;
}
#Override
public String toString() {
return data != null ? data.toString() : "[data null]";
}
#Override
public Iterator<N> iterator() {
// No clue what you want to do here, but your method will not work.
return children.iterator();
}
}
FileTreeNode
public class FileTreeNode extends TreeNode<FileTreeNode, File> {
public FileTreeNode(File root) {
super(root);
}
protected FileTreeNode(File data, FileTreeNode parent) {
super(data, parent);
}
public long calculateFileSize() {
return streamBranch().mapToLong(value -> value.getData().length()).sum();
}
#Override
protected CharSequence printNode() {
return getData().getName() + " " + calculateFileSize() + " bytes";
}
#Override
protected List<FileTreeNode> initChildren() {
File file = getData();
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
return Arrays.stream(files)
.sorted(Comparator.comparing(File::isFile).thenComparing(Comparator.naturalOrder()))
.map(path -> new FileTreeNode(path, this))
.toList();
}
}
return Collections.emptyList();
}
#Override
protected FileTreeNode instance() {
return this;
}
}
Usage
File file = new File("C:\\dir");
FileTreeNode rootNode = new FileTreeNode(file);
System.out.println(rootNode.renderBranch());
This way you can easily implement a TreeNode structure for other classes too. For example File is outdated and replaced by Path so maybe you will want a PathTreeNode in the future. Who knows.
I have a complex entity structure. Which contains the ID of the previous item ("previodElementId")
interface IPreviousElementEntity<PK> {
public void setId(PK id);
public PK getId();
public void setPreviousElementId(PK previousElementId);
public PK getPreviousElementId();
}
After receiving all entities from DB, I need to convert the resulting list into a linked list, and the linked list should be organized by the previous id.
I wrote the following code for conversion:
static <T extends IPreviousElementEntity> LinkedList<T> getLinkedListByPreviousId(Collection<T> collection) {
LinkedList<T> linkedList = new LinkedList<>();
if (collection == null || collection.isEmpty())
return linkedList;
// first find root element
collection.stream()
.filter(element -> element.getPreviousElementId() == null)
.forEach(linkedList::add);
if (linkedList.isEmpty()) return linkedList;
// TODO: convert to use stream. Please help!
Boolean isRun = true;
while (isRun) {
for (T element : collection) {
isRun = false;
if (linkedList.getLast().getId().equals(element.getPreviousElementId())) {
linkedList.add(element);
isRun = true;
break;
}
}
}
return linkedList;
}
But this code is terrible! Is it possible to write all these transformations on a stream? I especially want to get rid of the thundering while loop.
My full code:
import java.util.*;
public class App {
public static void main(String[] args) {
Entity entity1 = new Entity(3L, 2L, "third");
Entity entity2 = new Entity(2L, 1L, "second");
Entity entity3 = new Entity(4L, 3L, "forth");
Entity entity4 = new Entity(1L, null, "first");
List<Entity> entities = new ArrayList<>();
entities.add(entity1);
entities.add(entity2);
entities.add(entity3);
entities.add(entity4);
LinkedList<Entity> linkedListByPreviousId = getLinkedListByPreviousId(entities);
System.out.println(linkedListByPreviousId);
}
private static <T extends IPreviousElementEntity> LinkedList<T> getLinkedListByPreviousId(Collection<T> collection) {
LinkedList<T> linkedList = new LinkedList<>();
if (collection == null || collection.isEmpty())
return linkedList;
// first find root element
collection.stream()
.filter(element -> element.getPreviousElementId() == null)
.forEach(linkedList::add);
if (linkedList.isEmpty()) return linkedList;
//TODO: convert to use stream. Please help!
Boolean isRun = true;
while (isRun) {
for (T element : collection) {
isRun = false;
if (linkedList.getLast().getId().equals(element.getPreviousElementId())) {
linkedList.add(element);
isRun = true;
break;
}
}
}
return linkedList;
}
}
interface IPreviousElementEntity<PK> {
public void setId(PK id);
public PK getId();
public void setPreviousElementId(PK previousElementId);
public PK getPreviousElementId();
}
class Entity implements IPreviousElementEntity<Long> {
private Long id;
private Long previousElementId;
private String name;
public Entity(Long id, Long previousElementId, String name) {
this.id = id;
this.previousElementId = previousElementId;
this.name = name;
}
#Override
public Long getId() {
return id;
}
#Override
public void setId(Long id) {
this.id = id;
}
#Override
public Long getPreviousElementId() {
return previousElementId;
}
#Override
public void setPreviousElementId(Long previousElementId) {
this.previousElementId = previousElementId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Entity entity = (Entity) o;
return Objects.equals(id, entity.id) &&
Objects.equals(previousElementId, entity.previousElementId) &&
Objects.equals(name, entity.name);
}
#Override
public int hashCode() {
return Objects.hash(id, previousElementId, name);
}
#Override
public String toString() {
final StringBuilder sb = new StringBuilder("Entity{");
sb.append("id=").append(id);
sb.append(", previousElementId=").append(previousElementId);
sb.append(", name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
}
The while loop is nasty because it attempts to do an O(n^2) operation using a list, and continually repeating until there are no more options.
An O(n) operation is more suitable, through the use of a Map using previousElementId as a key.
if (linkedList.isEmpty()) return linkedList;
//create a map with previousElementId as Key, T as Object
Map<Integer, T> map = collection.stream().collect(
Collectors.toMap(T::getPreviousElementId, Function.identity()));
//we fetch nodes using the current ID as the key
T node = map.get(linkedList.getLast().getId());
while(node != null) {
linkedList.add(node);
node = map.get(node.getId());
}
You have a common use case and I think you should be able to come up with cleaner solution using streams.
Here is approach with stream only:
private static < T extends IPreviousElementEntity<?> > LinkedList<T> getLinkedListByPreviousId(
Collection<T> collection) {
//first create map with previous id mapped to element, this assumes
//whatever id you use has proper implementation of equals and hashCode
Map<?, T> map = collection.stream()
.collect(
Collectors.toMap(
IPreviousElementEntity::getPreviousElementId, Function.identity(),
(i1, i2) -> i1 ) );
//then create infinite stream which starts with element that has null previous id
//and moves on to the next element that points to it via previous id
//since this is an infinite stream we need to limit it by the number of elements in the map
return Stream
.iterate( map.get(null), i -> map.get( i.getId() ) )
.limit( map.size() )
.collect( Collectors.toCollection(LinkedList::new) );
}
I would like to ask you for help. I have a Device object
public class Device {
public String name;
public String deviceId;
#JsonSerialize(using = CustomResourceSerializer.class)
public Map<String, Map<String, Object>> customResources;
}
My goal is to "extract" this map directly to Device Object. Firstly I used #JsonAnyGetter which worked well and Map was nested under field String of first map directly under Device object.
But I need more complex logic and I have two problems which I don't know how to solve.
Key of first map is for example "configuration/inputOne". With #JsonAnyGetter the example output is { "configuration/inputOne": { "rate":23 } }
What I need is nested structure based on delimiter, so
{ "configuration": { "inputOne": { "rate":23 } } }
This I was almost able to do easily with custom JsonSerializer
jsonGenerator.writeStartObject();
foreach(splited key)
jsonGenerator.writeObjectFieldStart(resourceUriItem);
foreach(value)
jsonGenerator.writeObjectField(k, v);
foreach(splitted key)
jsonGenerator.writeEndObject();
jsonGenerator.writeEndObject();
But final object looks like
{ "customResource": {"configuration": { "inputOne": { "rate":23 } } } }
CustomResource field is from Device object and I don't know how to get rid of it. As with JsonAnyGetter. That's the first problem.
As you see, I am splitting the key of the map to have more nested strucutre, so from the "configuration/inputOne" to { configuration { inputOne { .. } }. But the map customResources can have of course multiple items, so for example:
"configuration/inputOne"
"configuration/inputTwo"
"configuration"
Now you probably see where is the problem. As I am iterating over keys and I am creating nested structure, I will override it. So for example, firstly I will create object configuration, then inputOne and fill it with fields. Closing objects. Then second item in map, creating configuration object and inputTwo object. But with creation of configuration, I will delete the one previously created with inputOne.
Do you have any proposal how to solve this? Thanks.
You can turn your map into a type of a tree by splitting on the / and creating a parent child relationship on the split items.
Using the following class as a tree element / node.
class TreeElement {
private String key;
private Object value;
private List<TreeElement> children;
public TreeElement(String key) {
this.key = key;
}
// getters and setters here
public void addChild(TreeElement child) {
if (this.children == null) {
this.children = new ArrayList<TreeElement>();
}
this.children.add(child);
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.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;
TreeElement other = (TreeElement) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equalsIgnoreCase(other.key))
return false;
return true;
}
#Override
public String toString() {
return "TreeElement [key=" + key + ", value=" + value + ", children=" + children + "]";
}
}
And the following test code.
public static void main(String[] args) {
try {
// create the config1, config2, etc.. here
Device device1 = new Device();
device1.customResources = new HashMap<String, Map<String, Object>>();
device1.customResources.put("configuration/inputOne", config1);
device1.customResources.put("configuration/inputTwo", config2);
device1.customResources.put("configuration", config3);
device1.customResources.put("configuration", duplicateConfig3);
device1.customResources.put("otherConfig", otherConfig);
device1.customResources.put("thirdConfig1", thirdConfig1);
device1.customResources.put("thirdConfig1/inputOne", thirdConfig2);
device1.customResources.put("thirdConfig1/inputOne", duplicateThirdConfig2);
List<TreeElement> elements = new ArrayList<TreeElement>();
for (Map.Entry<String, Map<String, Object>> entry : device1.customResources.entrySet()) {
TreeElement element = generateElement(null, entry.getKey(), entry.getValue());
elements.add(element);
}
List<TreeElement> joinedElements = joinElements(elements);
for (TreeElement e : joinedElements) {
System.out.println(e.getKey() + " - " + e.getValue());
if (e.getChildren() != null) {
for (TreeElement c : e.getChildren()) {
System.out.println("\t" + c.getKey() + " - " + c.getValue());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
This method generates a TreeElement from a Map> variable.
private static TreeElement generateElement(TreeElement parent, String item, Map<String, Object> value) {
try {
List<String> tokens = new ArrayList<String>(Arrays.asList(item.split("/")));
TreeElement child = new TreeElement(tokens.get(0));
boolean parentWasNull = false;
if (parent == null) {
parent = child;
parentWasNull = true;
}
if (tokens.size() > 1) {
if (parentWasNull == false) {
parent.addChild(child);
}
tokens.remove(0);
generateElement(child, StringUtils.join(tokens, "/"), value);
} else {
child.setValue(value);
if (parentWasNull == false) {
parent.addChild(child);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return parent;
}
This method joins common TreeElement objects into one parent and multiple children.
private static List<TreeElement> joinElements(List<TreeElement> elements) {
List<TreeElement> joinedElements = new ArrayList<TreeElement>();
for (TreeElement element : elements) {
if (joinedElements.contains(element) == true) {
// joined elment does not have children
if (joinedElements.get(joinedElements.indexOf(element)).getChildren() == null) {
joinedElements.get(joinedElements.indexOf(element)).setChildren(element.getChildren());
} else {
//joined element has children and the current element also has children
if (element.getChildren() != null) {
joinedElements.get(joinedElements.indexOf(element)).getChildren().addAll(element.getChildren());
}
}
/*
* set the value of joined element to the value of the current element; will overwrite
* any existing value if duplicates exist
*/
if (element.getValue() != null) {
joinedElements.get(joinedElements.indexOf(element)).setValue(element.getValue());
}
} else {
joinedElements.add(element);
}
}
return joinedElements;
}
I'm not sure how efficient this code is, but you get the below output which you can traverse in your custom serializer to print to JSON.
thirdConfig1 - {rate=30}
inputOne - {rate=3020}
configuration - {rate=1200}
inputOne - {rate=23}
inputTwo - {rate=50}
otherConfig - {rate=10}
I've written an XML parser which parses an XML documents and returns me a Map of and ID and Name. For some reason it's skipping duplicates IDs.
Edit:
public static Multimap<String,String> getMap(String pathToFile) {
Multimap<String,String> map = new ArrayListMultimap.create();
try {
Document doc = getDocument(pathToFile);
NodeList nList = doc.getElementsByTagName("Definition");
for(int i=0;i<nList.getLength();i++) {
Node nNode = nList.item(i);
if(nNode.getNodeType() == Node.ELEMENT_NODE) {
Element eElement = (Element) nNode;
String nodeID = eElement.getElementsByTagName("Key").item(0).getTextContent();
NodeList n = eElement.getElementsByTagName("Value");
for(int j=0;j<n.getLength();j++) {
String name = n.item(0).getTextContent();
if(name.equalsIgnoreCase("")) {
name = "blank"; // check for empty values
}
map.put(nodeID, name);
}
}
}
}
catch (IOException e) {
e.printStackTrace();
}
return map;
}
public static List<String> getIDList(String pathToFile) {
List<String> list = new ArrayList<String>();
Multimap<String, String> map = getMap(pathToFile);
for(String id : map.keySet()) {
list.add(id);
}
return list;
}
My question is, why is this happening? Why duplicate is being ignored?
because, it's a Map: the key is unique: in a Map, if you put(ID,"AA"); and put (ID,"BB"); only ID-"BB" remains. It's the philosophy: a Map maps a key to an unique value.
So dont use a Map.
You can use some pair, in a Set, or Vector or List, like that:
Set< Pair < String,String>>, or List< Pair >, Vector< Pair< String,String>>
Or you can use MultiMap (map with a key and multiple values):
How to create a Multimap<K,V> from a Map<K, Collection<V>>?
example with Multimap:
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
Multimap<String,String> mm=ArrayListMultimap.create(); //
mm.put("AAA", "123");
mm.put("AAA", "444");
mm.put("AAA", "555");
mm.put("BBB", "777");
// to use keySet
mm.keySet();
// getting values
Collection<String> values=mm.get("AAA");
for (String a_value: values) System.out.println("VALUE:"+a_value);
if you want to use Pair, you have to recreate get, keySet, ...
Pair the_pair=new Pair(ID,Name);
courtesy of : A Java collection of value pairs? (tuples?)
public class Pair<L,R> implements java.io.Serializable {
private final L left;
private final R right;
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
public L getLeft() { return left; }
public R getRight() { return right; }
#Override
public int hashCode() { return left.hashCode() ^ right.hashCode(); }
#Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof Pair)) return false;
Pair pairo = (Pair) o;
return this.left.equals(pairo.getLeft()) &&
this.right.equals(pairo.getRight());
}
}
I have an anchor pane with many text fields and other controls. I want to take values of all controls and their names and id.
For example: how can I clear all textfield values?
Straightforward solution would be just traversing all children of AnchorPane and looking for TextFields:
for (Node node : anchorPane.getChildren()) {
System.out.println("Id: " + node.getId());
if (node instanceof TextField) {
// clear
((TextField)node).setText("");
}
}
In case you need a recursive way test this,
public class NodeUtils {
public static <T extends Pane> Map<Node, Object> formValues(T parent) {
return formValues(parent, new HashMap<>());
}
private static <T extends Pane> Map<Node, Object> formValues(T parent, Map<Node, Object> map) {
for (Node node : parent.getChildren()) {
// Nodes - You can add more.
if (node instanceof TextField) {
map.put(node, ((TextField) node).getText());
}
if (node instanceof PasswordField) {
map.put(node, ((PasswordField) node).getText());
}
if (node instanceof TextArea) {
map.put(node, ((TextArea) node).getText());
}
if (node instanceof CheckBox) {
map.put(node, ((CheckBox) node).isSelected());
}
// Recursive.
if (node instanceof Pane) {
formValues((Pane) node, map);
}
}
return map;
}
}
Test source,
Map<Node, Object> formValues = NodeUtils.formValues(source);
Get only the nodes
public class NodeUtils {
public static ArrayList<Node> getAllNodes(Parent root) {
ArrayList<Node> nodes = new ArrayList<>();
addAllDescendents(root, nodes);
return nodes;
}
private static void addAllDescendents(Parent parent, ArrayList<Node> nodes) {
// Get children.
List<Node> children = Collections.EMPTY_LIST;
if (parent instanceof ButtonBar) {
children = ((ButtonBar) parent).getButtons();
} else if (parent instanceof TabPane) {
for (Tab tab : ((TabPane) parent).getTabs()) {
Node tabContent = tab.getContent();
if (tabContent instanceof Parent) {
addAllDescendents((Parent) tab.getContent(), nodes);
} else {
// You can log and get a type that is not supported.
}
}
} else {
children = parent.getChildrenUnmodifiable();
}
// Add nodes.
for (Node node : children) {
nodes.add(node);
if (node instanceof Parent) {
addAllDescendents((Parent) node, nodes);
}
}
}
}
Test source,
List<Node> nodes = NodeUtils.getAllNodes(aPaneOrAnotherParentObject);
Heres a Java 8 version:
anchorPane.getChildren()
.filtered(node -> node instanceof TextField)
.forEach(node -> ((TextField)node).setText(""));