Hierarchy data structure wih efficient query algorithm - java

I am looking for a data structure to represent hierarchy class system in Java.
For example, I have three class, University,Major,Student, and their relationship looks like below.
Is there a efficient data structure that I can query with a path-like expression?
For example, if the expression is CMU/cs/jakeļ¼Œthen I get a instance of student class whose name is jake. As far as I know, the Trie could do this, is there any other option?

If your data fits into memory then you can implement this by putting a Set of children in each node of the hierarchy and then walking the sets to determine if the path is valid, for example
class University {
private Set<Major> majors;
}
class Major {
private Set<Student> students;
}
class Main {
// true if the path is valid, else false
public boolean query(University university, Major major, Student student) {
return university.getMajors().contains(major) &&
major.getStudents().contains(student);
}
}
If you also need to walk the reverse path (i.e. if you need a bidirectional hierarchy) then you can put a Set of parents in each child.
This will run in average case O(d) where d is the depth of the hierarchy if you use HashSets, and in worst case O(d * lg(n)) where n is the size of the sets if you use TreeSets.
If your data doesn't fit into memory then you may want to consider using a graph database, e.g. Neo4j.
Edit: You can make the code more generic at the cost of type safety by using Map<String, E> at each level, assuming that each object has a unique name or some other string identifier.
abstract class Hierarchical<E extends Hierarchical> {
protected final Map<String, E> children;
public boolean query(Queue<String> query) {
String key = query.poll();
if(key != null) {
E value = map.get(key);
if(value != null) {
return query.isEmpty() || value.contains(query);
}
}
return false;
}
}
class University extends Hierarchical<Major> {}
class Major extends Hierarchical<Student> {}
// special case for the bottom of the hierarchy
class Student extends Hierarchical<Hierarchical> {
public Student() {
children = null;
}
#Override
public boolean query(Queue<String> query) {
throw new UnsupportedOperationException("query should never reach this depth");
}
}
class Main {
// true if the path is valid, else false
public boolean query(Hierarchial root, Queue<String> query) {
return root.contains(query);
}
}
This has the same runtime depending on whether you use a HashMap or TreeMap. The query only consists of a queue of strings; at each level of the hierarchy the first string is removed, the Map is queried and the child node is returned if found, and the query proceeds on to the child node until the queue is empty (return true) or a node isn't found (return false).

Related

Putting methods that handle HashMap of all instances of a class in a separate class

I have a class that creates index cards, and within it, I have an instance variable that is a static HashMap that stores all the instances created.
I have been thinking a lot about it and I thought that the methods that handle the opperations over that HashMap should go in a different class, because those methods don't opperate directly over any index card, they opperate over the list of index cards.
This way, I would have an IndexCard class, and an ListAdministrator class. And both classes would handle different functions.
The problem is that this new class (ListAdministrator) would only have static methods, because there is only one list and there is no reason to create any new list of index cards, I only need one.
Should I move those methods to another class or should I keep it like this? Is that a good practice?
This is the code:
class IndexCard {
public static HashMap <String, IndexCard> list = new HashMap <> ();
public String name;
public String address;
public String phone;
public String email;
public LocalDate dateRegister;
IndexCard(String name, String dni, String address, String phone, String email) {
this.name = name;
this.address = address;
this.phone = phone;
this.email = email;
dateRegister = LocalDate.now();
if (Utils.validarDni(dni) && !list.containsKey(dni)) {
list.put(dni, this);
} else {
throw new InvalidParameterException ("Error when entering the data or the DNI has already been previously registered");
}
}
/**
* Update the data of the selected card.
*/
public void update() throws IllegalAccessException {
String key = getKeyWithObject(this);
Scanner reader = new Scanner(System.in);
Field[] fields = this.getClass().getFields();
for (Field field: fields) {
String nameField = Utils.splitCamelCase(field.getName());
if (!Modifier.isStatic(field.getModifiers()) && (field.getType()).equals(String.class)) {
System.out.println ("Enter new " + nameField);
String value = reader.nextLine().trim();
field.set(this, value);
}
}
reader.close();
list.put(key, this);
System.out.println("Updated data \n \n");
}
/**
* Delete the selected card.
*/
public void delete() throws IllegalAccessException {
String key = getKeyWithObject(this);
Field [] fields = this.getClass().getFields();
for (Field field: fields) {
if (!Modifier.isStatic(field.getModifiers())) {
field.set(this, null);
}
}
list.remove(key);
}
/**
* Displays the data of the selected card on screen.
*/
public void print() throws IllegalAccessException {
Field [] fields = this.getClass().getFields();
for (Field field: fields) {
if (!Modifier.isStatic(field.getModifiers())) {
String nameFieldConSpaces = Utils.splitCamelCase(field.getName());
Object value = field.get(this);
System.out.println(nameFieldConSpaces + ":" + value);
}
}
}
/**
* Print all the entries of the desired sublist with the ID, Name and phone number.
*/
public static <T extends IndexCard> void SubClasslist (Class <T> subClass) {
for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
String key = entry.getKey ();
IndexCard card = entry.getValue ();
if (card.getClass().equals(subClass)) {
System.out.println ("ID:" + key + "| Name:" + card.name + "| Phone:" + card.phone);
}
}
}
/**
* Returns the object stored in the list of cards when entering the corresponding key.
*/
public static IndexCard GetObjetWithKey(String key) {
try {
return list.get(key);
} catch (IllegalArgumentException e) {
System.out.println (e + ": The indicated key does not appear in the database.");
return null;
}
}
/**
* Obtain the Key when entering the corresponding card.
*/
public static String getKeyWithObject (Object obj) {
for (HashMap.Entry <String, IndexCard> entry: list.entrySet()) {
if (obj.equals(entry.getValue())) {
return entry.getKey();
}
}
throw new IllegalArgumentException ("The indicated data does not appear in the database, and therefore we could not obtain the key.");
}
/**
* Returns a list of cards when entering the main data of the card.
* #param data Corresponds to the identifying name of the file.
*/
public static ArrayList <IndexCard> SearchByName (String data) {
try {
ArrayList <IndexCard> listCards = new ArrayList <> ();
for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
IndexCard card = entry.getValue ();
String name = entry.getValue().name;
if (name.toLowerCase().trim().contains(data.toLowerCase().trim())) {
listCards.add(card);
}
}
return listCards;
} catch (IllegalArgumentException e) {
System.out.println (e + "The indicated data does not appear in the database, you may have entered it incorrectly.");
return null;
}
}
}
All those static methods are what I would put in the new class.
This is how the new class ListAdministrator would look. It would not even need a constructor.
class ListAdministrator{
public static HashMap <String, IndexCard> list = new HashMap <> ();
/**
* Print all the entries of the desired sublist with the ID, Name and phone number.
*/
public static <T extends IndexCard> void SubClasslist (Class <T> subClass) {
for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
String key = entry.getKey ();
IndexCard card = entry.getValue ();
if (card.getClass().equals(subClass)) {
System.out.println ("ID:" + key + "| Name:" + card.name + "| Phone:" + card.phone);
}
}
}
/**
* Returns the object stored in the list of cards when entering the corresponding key.
*/
public static IndexCard GetObjetWithKey(String key) {
try {
return list.get(key);
} catch (IllegalArgumentException e) {
System.out.println (e + ": The indicated key does not appear in the database.");
return null;
}
}
/**
* Obtain the Key when entering the corresponding card.
*/
public static String getKeyWithObject (Object obj) {
for (HashMap.Entry <String, IndexCard> entry: list.entrySet()) {
if (obj.equals(entry.getValue())) {
return entry.getKey();
}
}
throw new IllegalArgumentException ("The indicated data does not appear in the database, and therefore we could not obtain the key.");
}
/**
* Returns a list of cards when entering the main data of the card.
* #param data Corresponds to the identifying name of the file.
*/
public static ArrayList <IndexCard> SearchByName (String data) {
try {
ArrayList <IndexCard> listCards = new ArrayList <> ();
for (HashMap.Entry <String, IndexCard> entry: list.entrySet ()) {
IndexCard card = entry.getValue ();
String name = entry.getValue().name;
if (name.toLowerCase().trim().contains(data.toLowerCase().trim())) {
listCards.add(card);
}
}
return listCards;
} catch (IllegalArgumentException e) {
System.out.println (e + "The indicated data does not appear in the database, you may have entered it incorrectly.");
return null;
}
}
}
You should keep the concerns of managing the IndexCards and the IndexCards themselves separate because of the Single Responsibility Principle. Furthermore the ListAdministrator should handle everything that deals with the management of the IndexCards, also deletion and creation of the managed objects.
The name ListAdministrator is somehow not meeting the point as it does not administrate lists, maybe use something like IndexCardRegistry.
To deal with concurrency you could use a ConcurrentMap as your main data storage.
Having ListAdministrator all static might come in handy if your IndexCards need access to it or other IndexCards, but this would not be the best design. Do they need to know anyway? From my understanding the IndexCards could be simple POJOs that contain only data and no logic at all.
On the other hand with an all-static ListAdministrator you will not be able to use two instances of managed objects at the same time in the future without major refactoring your code. Even if you never would expect this today a well defined object registry that can handle any object might come in handy in projects to come. Therefore I would rather use real instances for the ListAdministrator (and program against it's interface to stay flexible).
In more detail referring to your comments:
The idea of this approach is to keep concerns clearly separated, which will make future changes to your code feasible in case the project grows (most projects tend to do so). My understanding is that the ListAdministrator should manage your IndexCards. In a way this is the same as Object Relational Mappers work, but at the moment your database is a HashMap. If you create an interface for ListAdministrator you may even swap out the HashMap with a database without having to change its clients.
On second investigation of your code I found that IndexCards not only store the data but as well have methods to update the data. This represents another break of the Single Responsibility Principle and should be dealt with. If the ListAdministrator would provide an update method for a given IndexCard it could be used by as many different clients you can think of without changing any code behind the ListAdministrators API. Your first client would be the command-line interface you already have programmed, the next might be a web service.
With an all-static ListAdministrator you have one static Class that manages one static data set. It will always only deal with IndexCards, everything you add will end up in the same HashMap (if allowed/compatible). Every part of your application with access to the class ListAdministrator would have full access to the data. If you needed another ListAdministrator (handling create, delete, update, search) for a different type you would have to refactor everything to accomodate this or start duplicating code. Why not create an instance based solution in the first place. You would have your repository for IndexCards, and could add new repositories at will.
Maybe this is over-engineering for your use case but in keeping the responsibilities clearly separated you will find out that many extensions of your code will happen orthogonal (not affecting existing code), and this is where the fun really begins. And how do you want to practice this if not with smaller projects.
More details about the reason of using interfaces for flexible code (in response to latest comment)
The short answer is: always code against an interface (as stated in numerous articles and java books). But why?
A Java interface is like a contract between a class and its clients. It defines some methods, but does not implement them itself. To implement an interface you define a class with class XYZ implements SomeInterface and the source code of the class does whatever it finds reasonable to answer to the methods defined in the interface. You try to keep the interface small, to contain only the essential methods because the smaller the interface is, the less methods you have to take into account when changes have to be made.
A common idiom in Java would be to define a List<T> return type (the interface) for a method, which most likely would be an ArrayList (concrete class), but could be a LinkedList (another concrete class) as well, or anything else that implements the List interface. By just returning the List interface you prevent your client to use other methods of the otherwise returned concrete class as well which would greatly reduce your freedom to change the internal implementation of your "ListProvider". You hide the internal implementation but agree to return something that fulfills the given interface. If you want to conceed to even less obligations, you could return the interface Iteratable instead of List.
Checkout the Java API, you will find standard classes like ArrayList implement many interfaces. You could always use an ArrayList internally and return it as the smallest interface possible to do the job.
Back to your project. It would be essential to refer to the Registry (ListAdministrator) via its interface, not its concrete class. The interface would define methods like
interface IndexCardRegistry {
void delete(Long id) throws IllegalAccessException;
void update(Long id, Some data) throws IllegalAccessException;
// ...
}
What it does is of no concern for the client, it just hopes everything goes right. So if a client calls the repositories update method it would rely on the repository to update the targeted IndexCard. The repository could store the data as it wants, in a HashMap, in a List or even in a database, it would not matter to the clients.
class IndexCardMapBasedRegistry implements IndexCardRegistry {
private Map store = new HashMap();
void delete(Long id) throws IllegalAccessException {
// code to remove the IndexCard with id from the hashmap
}
void update(Long id, Some data) throws IllegalAccessException {
// code to get the IndexCard with id from
// the hashmap and update its contents
}
// ...
}
Now the new iteration, at creation of your registry you swap out IndexCardMapBasedRegistry for the new
class IndexCardDatabaseRegistry implements IndexCardRegistry {
private Database db;
void delete(Long id) throws IllegalAccessException {
// code to remove the IndexCard with id from the database
}
void update(Long id, Some data) throws IllegalAccessException {
// code to update the IndexCard with id in the database
}
// ...
}
IndexCardRegistry indexCards = new IndexCardMapBasedRegistry(); becomes
IndexCardRegistry indexCards = new IndexCardDatabaseRegistry();
The client must not change at all, but the Registry would be able to handle an amount of IndexCards that otherwise would blow your computers memory.
Stay with IndexCard class and dont need to create new class ListAdministrator
In class IndexCard you have list as of type hashmap and it represent in memory data structure and you have n number of method in this class to work in this data structure so i suggest stay with single class as it will serve single responsibility.

How to validate a list each time elements are added/removed?

Say I have the following class, with a list as a field:
class Group {
private List<Tile> tiles;
public Group(List<Tile> tiles) {
this.tiles = tiles;
}
public Group() {
this(new LinkedList<>());
}
public List<Tile> getTiles() {
return tiles;
}
}
What would be the best way to ensure the list is in a valid state each time elements are added/removed? (Currently the Tile class doesn't contain any setters so I don't need to worry about elements being modified.)
One possible way is to add a boolean field valid, and a method that updates valid, called after each time an element is added/removed:
class Group {
// ...
private boolean valid;
// ...
public void updateValid() {
// Check list is valid...
valid = true; // Updates `valid`
}
}
Example usage:
group.getTiles().add(new Tile());
group.updateValid();
However, with this there is the possibility of the valid field becoming inconsistent with the list (e.g. if one forgets to call updateValid after adding/removing an element).
Edit
I've since realised a simpler way is to just have a method returning a boolean rather than updating a boolean field, but I'm not sure if this is ideal as it's still possible for the list to be in an invalid state.
The safest solution is to expose individual methods controlling access to the list, instead exposing the entire object:
class Group {
private List<Tile> tiles;
public Group(List<Tile> tiles) {
// defensive copy
this.tiles = new LinkedList<>(tiles);
}
public Group() {
this.tiles = new LinkedList<>();
}
public boolean add(Tile tile) {
// validate *before* inserting
return validate(tile) && tiles.add(tile);
}
}
If your validation logic involves other elements in the list, you can calculate it after inserting:
public void add(Tile tile) {
tiles.add(tile);
updateValid(); // or throw IllegalStateException
}
Another approach would be to use lazy validation instead of expecting the client to call an extra method:
public boolean isValid() {
boolean valid = ... // validation logic here
return valid;
}
Depending on your read/write ratio, this could be more or less expensive than the eager validation you're proposing.
The updateValid() method is a good idea to validate the adding/removing operation.
To force the constraint, this method should not be called by client of the class as in your usage example but by the internal of the class, that is : any method of Group that modifies the List should call the private updateValid() method.
Besides, which is the usefulness of the boolean valid field?
If a validation fails, should you not stop the processing and throw an exception ?
Otherwise, it means that the current state of the Group object could be inconsistent. Which seems undesirable.
You could do it for example :
private void updateValid() {
if (...){ // is not valid
throw new IllegalArgumentException("modification not valid");
}
}
At last, as shmosel said, you should also avoid to provide a public method that get the real list. You could do a defensive copy to avoid change by the clients :
public List<Tile> getTiles() {
return new LinkedList(tiles);
}

Java converting from Object to Subclass

Here is my code for Scene.java. It has different types of objects, all of which are included in one common ArrayList called targets. All of them share a toString() method that returns their identifier. I want to use the targets list to determine if there is any object in the scene that matches a given identifier, regardless of its type:
ArrayList<NPC> npcs = new ArrayList<NPC>();
ArrayList<Item> items = new ArrayList<Item>();
ArrayList<EnviromentalObject> enviromental_objects = new ArrayList<EnviromentalObject>();
ArrayList<Object> targets;
public Object check_for_target(String target_name){
targets.addAll(npcs);
targets.addAll(items);
targets.addAll(enviromental_objects);
for (Object target : targets){
if (target.toString() == target_name){
return target;
}
}
return null;
Here is the code in Game.java, which checks for a given identifier. If there is a match ion the current scene, I want to know the object's type and treat it as its true type. Right now, I have the following code, and I knew it wouldn't work, but maybe it'll help get my idea across.
Object target = current_scene.check_for_target(target_name);
if (target == null){
System.out.println(UNRECOGNIZED_TARGET_MESSAGE);
} else {
String target_type = target.getClass().getName();
target = (target_type) target;
}
What would be the correct way of getting the object's type and then being able to use that object's methods? Right now, I'm only given Object's methods. Do I create a superclass for NPC, Item, and EnviromentalObject?
Basically, you can check if an object is an instance of a specific class.
it could be something like this :
if( target instanceof NPC) {
System.out.println("target is a NPC");
}
else if( Target instanceof Item) {
System.out.println("target is an Item");
}
if( target instanceof EnviromentalObject) {
System.out.println("target is EnviromentalObject");
}
Edit: as we talked in the comments I think you can change your code to reach a better solution. The above code is still works but it can be a very good practice to using Design Patterns that are known as best practices in programming. For this situation think about using java interface and define share methods that each object could implements them by its need. In the simplest way they print their identifier. Let's use an example :
public interface SceneThings() {
public void printIdentifire();
public String doSomeOtherThings();
}
Each object can implements the above interface by it needs like :
public class Item implements SceneThing {
...
public void printIdentifire(){
//print its identifier here.
System.out.print("ID:ITEM###");
}
public String doSomeOtherThings(){
//do some other works !!!
}
...
}
for other items same as above. And then you can use a single array to keep them without worry about their origin class like this:
ArrayList<SceneThings> targets = new ...
SceneThing obj = new Item();
targets.add(obj);
I hope this can help you to define a better solution in your case.
One of the ways how it could be done it to declare a superclass or interface Target and use it to keep targets array, the full code sample with abstract class:
ArrayList<NPC> npcs = new ArrayList<NPC>();
ArrayList<Item> items = new ArrayList<Item>();
ArrayList<EnviromentalObject> enviromental_objects = new ArrayList<EnviromentalObject>();
ArrayList<Target> targets;
public Target check_for_target(String target_name) {
targets.addAll(npcs);
targets.addAll(items);
targets.addAll(enviromental_objects);
for (Target target : targets) {
if (target.toString().equals(target_name)) {
return target;
}
}
return null;
}
private abstract class Target {}
private class NPC extends Target {}
private class Item extends Target {}
private class EnviromentalObject extends Target {}

Spring is not serializing HashMap Attribute

First, I am using Spring MVC.
I have a "Skill"-modelclass, where I placed the #JsonIgnoreProperties
#JsonIgnoreProperties({"personSkills","berufsgruppes","skills"})
#JsonPropertyOrder({"idSkill", "name", "levelBezeichnung", "skill"})
I am using it because there are many-to-many or many-to-one or one-to-many relationships and without this property it causes an StackOverFlowException (Infinite Error). One skill can have many skills, so there is a kind of recursion.
I implemented an Sub-Class for "Skill" named "SkillBean", which has one more attribute "checked" thats just relevant for the application not for database.
public class Skill implements java.io.Serializable {
private Set<Skill> skills = new HashSet<Skill>(0);
...
#OneToMany(fetch = FetchType.LAZY, mappedBy = "skill")
public Set<Skill> getSkills() {
return this.skills;
}
public void setSkills(Set<Skill> skills) {
this.skills = skills;
}
public class SkillBean extends Skill implements Serializable{
public boolean checked;
public SkillBean() {
}
public SkillBean(Skill skill, boolean checked) {
this.checked = checked;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
}
Im Using BeanUtils.copyProperties() to copy a Skill-Object into a SkillBean-Object. This works fine. I need to reorder the skills because currently I get the lowest Child-Skill first and not its parent. For this, I am trying to reorder objects and trying to build a tree in a list. Every skill has a Set of its children.
private ArrayList<SkillBean> formatSkillMap(HashMap<Integer, SkillBean> map) {
Map<Integer, SkillBean> tempSkills = (Map<Integer, SkillBean>) map.entrySet().stream().filter(p -> p.getValue().getSkill() == null)
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
ArrayList<SkillBean> list = new ArrayList<SkillBean>(tempSkills.values());
for (int i = 0; i < list.size(); i++) {
SkillBean sb = list.get(i);
tempSkills = (Map<Integer, SkillBean>) map.entrySet().stream().filter(p -> p.getValue().getSkill() != null)
.filter(p -> p.getValue().getSkill().getIdSkill() == sb.getIdSkill()).collect(Collectors.toMap(Entry::getKey, Entry::getValue));
Set<Skill> test = new HashSet<Skill>(tempSkills.values());
list.get(i).setSkills(test);
}
return list;
But the list doesnt return the Sub-skillset Could anyone tell me why the subskills are not serialized? When I return the subset of this parent-skill it their subskills are serialized.
0: {
"idSkill": 34
"name": "Methodik"
"levelBezeichnung": {
"#id": 1
"idLevelBezeichnung": 1
"bezeichnung": "Standard"
"handler": {}
"hibernateLazyInitializer": {}
}-
"checked": true
}
Without reordering it looks sth like this, but the problem is that the skill with id=34 is the parent skill and 9 is the subskill. I want it exactly the other way around. There could be three levels.
9: {
"idSkill": 9
"name": "Standards"
"levelBezeichnung": {
"#id": 1
"idLevelBezeichnung": 1
"bezeichnung": "Standard"
"handler": {}
"hibernateLazyInitializer": {}
}-
"skill": {
"idSkill": 34
"name": "Methodik"
"levelBezeichnung": 1
}-
"checked": true
}
Finally, I end up with this:
Ignore parent of a skill or ignore children of a skill to avoid infinite recursion. In some case you don't need to ignore one of them. If you have not that much data it could work. I'am talking about 150 nodes where each node knows its parent/children.
I am querying for the path from bottom to top of my lowest skill with a custom sql query.
I am putting all my skills on the highest level in a map. That means, I have access to all my skills, cause (as I said) every node knows his children.
I am searching in my map from top to bottom and delete all references that I don't need based on the path, which I already got.
The whole code is a bit complex and I'm using recursion to made it less complex. In the end I am not that pleased with this solution because there are many loops in it and so far I am having some trouble with performance-issues.
I need to discover whether it is a database-query problem or a problem caused by the loops.

How Can I make This Algorithm Generic?

I am working on an object cache of CMS objects. I need to maintain a parent/child relationship between a Product and child objects (Options and Attributes). The parent (Product) is illustrated in the first code sample.
It is easy enough to do, but I am looking for a way to make the assignment of the child to the parent, as shown in the 2nd code block, generic.
Since all CMS objects extend CMSContent, I can use ProductID. However, is there a way to make the field (e.g. ProductAttribute) generic so that I can put the algorithm in a method and call the method with a parent and child object to make the attribute assignment?
I know that an ORM framework like Hibernate is appropriate here, but that won't fit since I have a fixed database structure.
public class Product extends CMSContent {
private List<ProductAttribute> productAttributes;
private List<ProductOptions> productOptions;
// getters,setters
}
Algorithm to match them up.
// attach Product Attributes to Product
for (Product p : listP) {
Map<String, Object> parameters = new HashMap<String, Object>();
for (ProductAttribute po : listPA) {
parameters.put("pid", p.getPid());
parameters.put("ref", po.getRid());
int i = jdbcTemplate.queryForInt(sqlAttr, parameters); // select count(*), 1 if matched.
if (i == 1) {
p.getProductAttributes().add(po); // generic field?
}
}
}
Wouldn't this two Methods in Product help
public void add(ProductAttribute item){
productAttributes.add(item);
}
public void add(ProductOption item){
productOption.add(item);
}
so you should be able to just add a ProductAttribute or a ProductOption

Categories