Somewhat new to Java. I have used various Java collections (treeset, hashmap, arraylist) before quite successfully. My problem is similar to a Facebook-like network. I have various users in a membership organization and I want to store in a collection for each individual in our membership other members who are linked to this member by interest. I thought the simplest solution would be to dynamically allocate a new simple collection by name for each member that would have other member names (existing or new) linked, but it appears Java does not allow dynamic allocation of new collections.
I could have a concatenated string in a hashmap listing all the names associated with the key name, but this seems an akward solution. I assume this is a social common network-like problem that has an elegant solution. Suggestions?
Why don't you model it like a graph?
class Node {
private String name;
// TODO: Write your getters / setters.
}
class Edge {
private Edge source, destination;
// TODO: Write your getters / setters.
}
List<Node> nodes = new ArrayList<Node>();
List<Edge> edges = new ArrayList<Edge>();
Then, if you encounter a relationship you can do the following:
Node alice = new Node("Alice Kentucky");
if (!nodes.contains(alice)) { nodes.add(alice); }
edges.add(new Edge(bob, alice)); // where Bob is already in the node list
Related
I have data as shown as below. Here if Team 1 is parent & having 2 child Team A & Team B. Team A is again a parent & having player names as child. Team B does not have any child.
Again in another scenario, Team A is independent parent & contains some child etc..
If i give Team 1, then it should fetch records of Team A & Team B as a bundle.
If i give Team A, then it should fetch records of Team A containing its child.
I was thinking to implement this using Map or Tree . and I tried this -
public class Node {
private String id;
private List<Node> children = new ArrayList<>();
private Node parent;
..........
//setters and getters
}
but here creating node dynamically is problem because we don't know the levels of parents(in this example there are 2). means "Dhoni" again contains some child like wise.
How to implements this ?. Please guide.
Whatever i understood from problem description i will try to summarize here.You are looking for a data structure which can take parent name(key) and it might have children, and each child also further can be extended.
public class Node {
private String id; // for each level you have key defined.
private List<Node> children = new ArrayList<>(); //using given key you can get children list
}
You can use map here
Map<String, List<Node>> // here key is team name etc., and list represents children.
If you give team1 as key, you get list which contains teamA, teamB. So if you want to check further, check list size, if it is greater than zero, you can get children(Further you can get all the players defined for both teamA,teamB) otherwise you are at last child.
I am trying to replace element in collection with new modified version. Below is short code that aims to demonstrate what I'd like to achieve.
The whole idea is that I have one object that consists of collections of other objects. At some point in time I am expecting that this objects in collections (in my example phones) might require some modifications and I'd like to modify the code in one place only.
I know that in order to update the object's attributes I can use setters while iterating through the collection as demonstrated below. But maybe there is better, more general way to achieve that.
public class Customer {
private int id;
private Collection<Phone> phoneCollection;
public Customer() {
phoneCollection = new ArrayList<>();
}
//getters and setters
}
and Phone class
public class Phone {
private int id;
private String number;
private String name;
//getters and setters
}
and
public static void main(String[] args) {
Customer c = new Customer();
c.addPhone(new Phone(1, "12345", "aaa"));
c.addPhone(new Phone(2, "34567", "bbb"));
System.out.println(c);
Phone p = new Phone(2, "9999999", "new name");
Collection<Phone> col = c.getPhoneCollection();
for (Phone phone : col) {
if (phone.getId() == p.getId()) {
// This is working fine
// phone.setNumber(p.getNumber());
// phone.setName(p.getName());
// But I'd like to replace whole object if possible and this is not working, at least not that way
phone = p;
}
}
System.out.println(c);
}
}
Is this possible to achieve what I want?
I tried copy constructor idea and other methods I found searching the net but none of them was working like I would expect.
EDIT 1
After reading some comments I got an idea
I added the following method to my Phone class
public static void replace(Phone org, Phone dst){
org.setName(dst.getName());
org.setNumber(dst.getNumber());
}
and now my foreach part looks like that
for (Phone phone : col) {
if (phone.getId() == p.getId()) {
Phone.replace(phone, p);
}
}
And it does the job.
Now if I change the Phone class attributes I only need to change that method. Do you think it is OK solving the issue that way?
You should not modify the collection while you're iterating through it; that's likely to earn you a ConcurrentModificationException. You can scan the collection for the first object that matches your search criterion. Then you can exit the loop, remove the old object, and add the new one.
Collection<Phone> col = c.getPhoneCollection();
Phone original = null;
for (Phone phone : col) {
if (phone.getId() == p.getId()) {
original = phone;
break;
}
}
if (original != null) {
Phone replacement = new Phone(original);
replacement.setNumber(p.getNumber());
replacement.setName(p.getName());
col.remove(original);
col.add(replacement);
}
Alternatively, you could declare a more specific type of collection, such as a List, that would allow you to work with indexes, which would make the replacement step much more efficient.
If your phone IDs are unique to each phone, you should consider using a Map<Integer, Phone> that maps each phone ID to the corresponding phone. (Alternatively, you could use some sort of third-party sparse array structure that doesn't involve boxing each ID into an Integer.) Of course, if your IDs aren't unique, then you might want to modify the above to gather a secondary collection of all matching phones (and reconsider the logic of your existing code as well).
You can also use a Set (HashSet), this is only when you don't want to do the way Mike suggested.
Use the Phone as an item in the set. Don't forget to implement hashCode() and equals() in Phone. hashCode() should return the id, as it is supposed to be unique.
Since you are concerned about replacing the item, here's how HashSet will help you :
Create an instance of your object.
Remove the object you want to replace from the set.
Add the new object (you created in step 1) back to the set.
Both these operations 2 & 3 are guaranteed in O(1) / constant time.
You don't need to maintain a map for this problem, that's redundant.
If you want to get the object from the collection itself and then modify it, then HashMap would be better, search is guaranteed in O(1) time.
Instead of a list, use a map with the Phone's id as the key. Then your code looks like this:
public static void main(String[] args) {
Customer c = new Customer();
c.addPhone(new Phone(1, "12345", "aaa"));
c.addPhone(new Phone(2, "34567", "bbb"));
System.out.println(c);
Phone p = new Phone(2, "9999999", "new name");
Map<Integer, Phone> phoneMap = c.getPhoneMap();
phoneMap.put(p.getId(), p);
System.out.println(c);
}
If you take the object out from the collection and update its properties, it will get reflected in the same object in collection too.. Hence, you dont have to technically replace object after updating it.
As "Mike M." pointed out, you can use hashmap to retrieve the object quickly without iteration and update the object values.
If order matters to you, you can change Collection to List (Since you're always using an ArrayList anyway) and then:
int index = col.indexOf(phone);
col.remove(phone);
col.add(p, index);
There is a HashMap called flights and an ArrayList called planes. The method is meant to create a new flight if the user enters the name of a plane that exists in this.planes. The only problem is that, with this method, there is no way to create two flights with the same plane and different arrival/departures. When you create a new Flight object with the same plane as a previous one, it REPLACES that previous one. I'm wondering how I can keep that from happening.
public void addFlight(String planeID, String departure, String arrival) {
Flight newFlight = null;
for(Plane p : this.planes) {
if(p.getID().equals(planeID)) {
newFlight = new Flight(p, departure, arrival);
}
}
flights.put(planeID, newFlight);
}
Since the planeID String being entered determines the plane from this.planes that is assigned to the new Flight, I made a loop that runs through the planes ArrayList to check if the name matches. I'm pretty sure this is what's keeping me from being able to make multiple "Flights" that have the same "Plane" but I don't know what else I can change. I tried adding in this additional if statement but it didn't do what I thought it would.
else if (this.flights.keySet().contains(p.getID())) {
newFlight = new Flight(p, departure, arrival); //In case it is a repeated Plane
}
You need this
HashMap<String,List<Flight>>
instead of
HashMap<String,Flight>
This will help you maintain more than one Flight instances in the list mapped with the planeID
Flights is a HashMap. This is a data structure that stores items in key/value pairs. In your case, planeID is the key. So when you are inserting a new flight, it will check the map's contents, see the existing Plane ID & insert your new flight in it's place. It's how the data structure works.
You need to use something more unique as the key. An object combining the plane ID & some departure details perhaps?
Create HashMap for planes and ArrayList of flights in stead.
Your design will much more be simplified and you'll get the expected behavior.
Adding code would become
public void addFlight(String planeID, String departure, String arrival) {
flights.add(planes.get(planeID), departure, arrival);
}
No need to search, check or worry.
Good luck.
Edit in response to the comment:
You are using plane id's as keys to the HashMap. So whichever is unique w.r.t. the key, should be placed in the HashMap. Which in this case is plane.
For storing flights, there were two choices
Aggregation: HashMap<Plane, List<Flight>> as suggested by Octopus
Association: List<Flight> with Plane as a member of Flight class.
Both choices are easy to use, but in first choice, your plane reference inside flight class is redundant. So I'd go for second choice.
You should choose whichever suits you best.
Hope this helps.
My graph contains nodes called points and lines.
There is a relationship type called "NEXT", which connects two points and has a property called lineID (a long). A line node consists simply of an ID and a reference to a "root" point. To traverse a line is to start with its root node and follow the NEXT relationships whose lineID matches the id of the line being traversed. To clarify, if we're traversing a line with ID 123, whose root point has id 321, the Cypher traversal would be:
START n = node(321)
MATCH (n)-[rel:NEXT*{lineID:123}]->(x)
RETURN collect(rel)
A line, then, is essentially a linked list of Next relationships with matching lineID properties. That said, I don't want to persist this list as a property of lines - I want the list to be constructed by a traversal when a line is loaded.
What are my options for implementing this in spring-data-neo4j? Specifically, should "lines" exist as NodeEntitys, and if so what should they contain?
#NodeEntity
class Line {
#RelatedTo(type="ROOT")
Point root;
#RelatedToVia(type="NEXT")
Iterable<Item> list;
doesn't quite fit, because the line is not related via Next relationships to the item, the root point is. It also fails to address the fact that those NEXT relationships need to have a lineID property matching the line's ID (which becomes important because some points exist on multiple lines - i.e. they have multiple NEXT relationships with different lineID's). I have a hunch that the solution will involve annotating the list as a #GraphTraversal, but I don't understand how that would work.
I'm doing this largely as an exercise to wrap my head around data modeling in SDN, in the context of wrapping my head around Neo4j and graph databases in general. If the question I'm asking reveals a flaw in my understanding of any of these things, I'd be very appreciative if someone could point it out.
This should be a suitable model for your entities:
#NodeEntity
class Point {
#GraphId
protected Long id;
#RelatedToVia(type="NEXT")
Set<Edge> edges;
}
#NodeEntity
class Line {
#GraphId
protected Long id;
#RelatedTo(type="ROOT")
Point root;
}
#RelationshipEntity
public class Edge {
#GraphId
protected Long id;
#StartNode private Point from;
#EndNode private Point to;
#RelatedTo(type="LINE")
Line line;
}
It easily allows both programmatic navigation in Java as in:
Set edges = line.getPoint().getEdges();
for (Edge edge: edges) {
if (edge.getLine().getId() == id) {
...
}
}
or Cypher queries like the one you listed.
I need to develop a simple cache (no concurrency or refresh required) to hold different types of objects. The lookup of these objects may be in a different way. Like lets say we are caching book object which has ISBN number and author. Lookup of this object can be either by ISBN number like
Book lookupBookByISBN(String isbn);
OR it could be a lookupByAuthor like
List lookupBookByAuthor(String authorName);
In a very simple way, it means I can have a Cache object which has two maps one to store book object by ISBN and another to store the same object by authorname.
Like this, think of many such object type like book, so I do not want to store the same object in different maps just because the lookup of them are different.
One way I was thinking of having a single Map whose key is a custom Key object and value is Object (so that I can store any object or list of object)
The Key object is a immutable object which might look like this
public class Key {
private final Stirng keyName;
private final String keyValue;
public Key(String name,String value) {
this.keyName= name;
this.keyValue = value;
}
//getters for keyName and value
//hashcode and equals to be put as a key of a map
}
Implementation of lookup method will be
public Book lookupBookByISBN(String isbn) {
Key key = new Key("ISBN",isbn);
return ((Book)map.get(key));
}
public List<Book> lookupBookByAuthor(String isbn) {
Key key = new Key("Author",isbn);
return (List<Book>map.get(key));
}
The insert into map needs to be carefully done as the same object needs to be inserted twice into the map.
public void putBook(Book book) {
Key key = new Key("ISBN",book.getISBN());
map.put(key,book);
key = new Key("Author",book.getAuthor());
List<Book> list = map.get(key);
if (null == list) {
list = new ArrayList<Book>();
map.put(key,book);
}
list.add(book);
}
I somehow feel this might not be a good idea and I might need to put the same object in the map N number of times depending upon N dimensions by which I need to lookup the object.
Is there anyother way to design the same in a better way?
When you store an object in a collection (of any kind), you only store a reference to the object. So go ahead and use multiple maps, you will have only one copy of the actual object.
For example
Map<String,MyBigObject> map1 = new HashMap...
Map<String,MyBigObject> map2 = new HashMap...
MyBigObject mbo = new MyBigObject(...);
map1.put(mbo.getISBN(),mbo);
map2.put(mbo.getAuthor(),mbo);
The single object mbo is now accessible via either map.
EDIT: If you're worried about the complexity of multiple maps complicating the code, write a class MultiMap that contains all the maps and manages them in whatever way you want. You could have methods add(MyBigObject...) which inserts the object into all the maps using the various property accessors to set the correct key, and then lookup methods such as getByAuthor(...) and getByISBN(...), and whatever else you need. Hide all the complexity behind a simple unified interace.