I'm completely new and right now I'm writing a bid of code for university. I want to create an open hashtable and I wrote this peace of code:
public class AuDOpenHashTable extends AuDHashTable {
private LinkedList<Contact>[] table;
public AuDOpenHashTable(int capacity) {
super(capacity);
this.table = new LinkedList[capacity];
}
#Override
public void insert(Contact c) {
int position = hash(c.email);
if (table[position] == null) {
table[position] = new LinkedList<>();
}
table[position].add(c);
}
#Override
public void remove(Contact c) throws NoSuchElementException{
int position = hash(c.email);
if(table[position] != null){
table[position].remove();
}
else{
throw new NoSuchElementException();
}
}
#Override
public Contact getContact(String email)throws NoSuchElementException{
int position = hash(email);
table[position].getContact(email);
if(table[position] != null){
return table[position].get(position);
}
else{
throw new NoSuchElementException();
}
}
}
public abstract class AuDHashTable {
protected int capacity;
public AuDHashTable(int capacity){
this.capacity = capacity;
}
public abstract void insert(Contact c);
public abstract void remove(Contact c);
public abstract Contact getContact(String email);
protected int hash(String s){
int hash = 0;
for(int i = 0; i < s.length(); i++){
hash += s.charAt(i);
}
hash = hash % capacity;
return hash;
}
public static void main(String[] args) {
AuDClosedHashTable hashtabelle = new AuDClosedHashTable(3);
Contact eins = new Contact("hans.peter#web.de");
Contact zwei = new Contact("selina.meier#gmail.com");
Contact drei = new Contact("alexander.bauer#gmx.de");
hashtabelle.insert(eins);
hashtabelle.insert(zwei);
hashtabelle.insert(drei);
System.out.println(hashtabelle.isFull());
System.out.println(hashtabelle.getIndexOf("hans.peter#web.de"));
System.out.println(hashtabelle.getIndexOf("selina.meier#gmail.com"));
System.out.println(hashtabelle.getIndexOf("alexander.bauer#gmx.de"));
hashtabelle.remove(drei);
System.out.println(hashtabelle.isFull());
System.out.println(hashtabelle.getContact("selina.meier#gmail.com"));
System.out.println(hashtabelle.getContact("hans.peter#web.de"));
System.out.println(hashtabelle.getContact("alexander.bauer#gmx.de"));
AuDOpenHashTable hashtabelle = new AuDOpenHashTable(3);
Contact eins = new Contact("hans.peter#web.de");
Contact zwei = new Contact("selina.meier#gmail.com");
Contact drei = new Contact("alexander.bauer#gmx.de");
hashtabelle.insert(eins);
hashtabelle.insert(zwei);
hashtabelle.insert(drei);
System.out.println(hashtabelle.getContact("selina.meier#gmail.com"));
hashtabelle.remove(zwei);
System.out.println(hashtabelle.getContact("selina.meier#gmail.com"));
}
}
So, my problem is in the "getContact()" method. If i want to display an account on a certain position and it is the ONLY account on that position, then everything works fine. But, if want to display an account in which the head differs the tail, so there are two accounts, it only give me one account(mostly not the correct one). For these examples the code works very well, but if i decide to pick other names, sometimes it does also not work. But not to make it complicated I wanted to hear your suggestions on how I can improve the "getContact" method. Thanks in prehand.
The hash function will tell you which bucket an item can be in, but you still need to check all the items within the bucket for equality. getContact should iterate over the LinkedList and check the email against each contact, then only return the contact with the matching email. Same for the remove method.
Different keys can have the same hash code. This is detected at insertion usually in which case there's usually a rehash, some algorithm to produce another hash code, that results in another possibly free has code. If not free it is again rehashed. If this continues a lot then the table was possibly allocated to small and a bigger table should be used.
When retrieving the information, you should compare the data at the index with the key searched. If not matching, rehash ( same algorith as insert ) and try again. Until you find it or end up in an empty index, in which case the key was not there.
Related
I'm sitting on an assignment for university and I'm at a point, where I fear I haven't really understood something fundamental in the concecpt of Java or OOP altogether. I'll try to make it as short as possible (maybe it's sufficient to just look at the 3rd code segment, but I just wanted to make sure, I included enough detail). I am to write a little employee management. One class within this project is the employeeManagement itself and this class should possess a method for sorting employees by first letter via bubblesort.
I have written 3 classes for this: The first one is "Employee", which contains a name and an ID (a running number) , getter and setter methods and one method for checking whether the first letter of one employee is smaller (lower in the alphabet) than the other. It looks like this:
static boolean isSmaller(Employee source, Employee target) {
char[] sourceArray = new char[source.name.length()];
char[] targetArray = new char[target.name.length()];
sourceArray = source.name.toCharArray();
targetArray = target.name.toCharArray();
if(sourceArray[0] < targetArray[0])
return true;
else
return false;
}
I tested it and it seems to work for my case. Now there's another class called EmployeeList and it manages the employees via an array of employees ("Employee" objects). The size of this array is determined via constructor. My code looks like this:
public class EmployeeList {
/*attributes*/
private int size;
private Employee[] employeeArray;
/* constructor */
public EmployeeList(int size) {
this.employeeArray = new Employee[size];
}
/* methods */
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
/* adds employee to end of the list. Returns false, if list is too small */
boolean add(Employee m) {
int id = m.getID();
if (id > employeeArray.length) {
return false;
} else {
employeeArray[id] = m;
return true;
}
}
/* returns employee at certain position */
Employee get(int index) {
return employeeArray[index];
}
/* Sets employee at certain position. Returns null, if position doesn't exist. Else returns old value. */
Employee set(int index, Employee m) {
if (employeeArray[index] == null) {
return null;
} else {
Employee before = employeeArray[index];
employeeArray[index] = m;
return before;
}
}
Now comes my real problem: In a third class called "employeeManagement" I am supposed to implement the sorting algorithm. The class looks like this:
public class EmployeeManagement {
private EmployeeList ml = new EmployeeList(3);
public boolean addEmployee(Employee e) {
return ml.add(e);
}
public void sortEmployee() {
System.out.println(ml.getSize()); // I wrote this for debugging, exactly here lies my problem
for (int n = ml.getSize(); n > 1; n--) {
for (int i = 0; i < n - 1; i++) {
if (Employee.isSmaller(ml.get(i), ml.get(i + 1)) == false) {
Employee old = ml.set(i, ml.get(i + 1));
ml.set(i+1, old);
}
}
}
}
The "println" before my comment returns "0" in console... I am expecting "3" as this is the size I gave the "EmployeeList" as parameter of the constructor within my "EmployeeManagement" class. Where is my mistake ? And how can I access the size of the object I created in the "EmployeeManagement" class (the "3") ? I'm really looking forward to your answers!
Thanks,
Phreneticus
You are not storing size in your constructor. Something like,
public EmployeeList(int size) {
this.employeeArray = new Employee[size];
this.size = size; // <-- add this.
}
Also, setSize isn't going to automatically copy (and grow) the array. You will need to copy the array, because Java arrays have a fixed length. Finally, you don't really need size here since employeeArray has a length.
The size variable you are calling is the class field. If you take a quick look at your code, the getter is getting the field (which is initialized as zero when created). The size you are using it. The good way of doing it would be to get the size of the array in the getter like this:
public int getSize() {
return employeeArray.length;
}
This would return the size of the array in the object.
I have two different ArrayList instances, one of type Container and one of type String. The first is a list of "banned goods" (strings) for a country, and the other is a list of containers on a ship. The ship travels through the country, and the containers are searched for the banned goods. If the container contains the banned goods, that container should be removed/deleted.
public Customs(String country)
{
countryName = country;
bannedGoods = new ArrayList<String>();
}
public Ship(String n, double weight)
{
emptyWeight = totalWeight = weight;
name = n;
containers = new ArrayList<Container>();
}
I already have a method in the Ship class that removes the container:
public void removeContainer(int i)
{
if(i >= 0 && i < containers.size()) {
Container r = containers.remove(i);
totalWeight = totalWeight - r.getWeight();
}
}
I am trying to create an method to inspect the ship for the containers. I want to use two for-loops for each of the arrays, but I can't seem to get it right! Can someone help me use the two loops to search the arrays? Additionally, I think that I will need to use an iterator (the remove function, specifically) in the loop, but that is also confusing for me. Should the iterator remove method replace the method I already wrote in class ship? Here is what I have:
public void inspect(Ship ship)
{
for (String good : bannedGoods) {
for (String con : containers) {
if (con.contains(good) {
container.remove();
}
}
}
And here is my attempt at the iterator:
for(String good : bannedGoods) {
Iterator<String> it = ship.containers.iterator();
while (it.hasNext())
if (ship.contains(good))
it.remove();
}
I don't think you need 2 for loops. You should iterate over banned goods & simply remove it from the containers.
Also, Assuming that containers list is of type string as this is mentioned in your fist line : I have two different arrayLists of the same type String
public void inspect(Ship ship, ArrayList<String> bannedGoods){
if (ship == null || bannedGoods == null || bannedGoods.isEmpty())
return;
for(String good : bannedGoods){
ship.containers.remove(good);
}
}
If, Containers is of type Container and it contains a list of containers(Arraylist of string) which is accessible via the method get_containers(), the following would work:
public void inspect(Ship ship, ArrayList<String> bannedGoods){
if (ship == null || bannedGoods == null || bannedGoods.isEmpty())
return;
for(String good : bannedGoods){
for(Container container : ship.containers){
container.get_containers().remove(good);
}
}
}
You can stick to the methods you are using at the moment. But keep in mind that you either need to use the iterator's remove method or not use iterators. So to use your remove method, either implement Iterable or simply use indexes instead of iterators:
for (int i = 0; i < bannedGoods.size(); i++)
{
for (int j = 0; j < containers.size();) // NOTE: no j++ here
{
Container c = containers.get(j);
if (c.contains(bannedGoods.get(i))
c.removeContainer(j);
else
j++; // only if you don't remove the container increment
// j - when removing the next element gets current
// index
}
}
You're actually pretty close, and you've done a good job of focusing on object-oriented programming principles while designing your classes. I think the things you need to focus on now are just being more careful with your types. Below are some suggested modifications to your classes (Container isn't shown, but I'm assuming it has a public boolean contains (String s) method that checks whether the container has a certain good s inside.
import java.util.*;
public class Ship implements Iterable<Container> {
private double emptyWeight, totalWeight, weight;
private String name;
private List<Container> containers = new ArrayList<Container>();
public Ship(String n, double weight) {
emptyWeight = totalWeight = weight;
name = n;
}
private void removeContainer(int i) {
if (i >= 0 && i < containers.size()) {
Container r = containers.remove(i);
totalWeight = totalWeight - r.getWeight();
}
}
public Iterator<Container> iterator() {
return new Iterator<Container> {
private index = 0;
private Container previous = null;
public boolean hasNext() {
return index < containers.size();
}
public Container next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
previous = containers.get(index++);
return previous;
}
public void remove() {
if (previous == null) {
throw new IllegalStateException();
}
removeContainer(containers.indexOf(previous));
previous = null;
}
};
}
}
I suggest keeping removeContainer within your Ship class since it's responsible for keeping track of how its weight changes when a container is removed. For the same reason, don't allow external classes to directly access its containers list. That way you can prevent other code from adding or removing values from that list without updating the weight correctly. I'd suggest making the containers list private, and expose an Iterator to allow users of the class to interact with the containers.
Within your Customs class, you'd use the Iterator's remove method to remove offending Container instances:
import java.util.*;
public class Customs {
private String countryName;
private List<String> bannedGoods = new ArrayList<String>();
public Customs(String country) {
countryName = country;
}
public void inspect(Ship ship) {
for (String good : bannedGoods) {
for (Iterator<Container> it = ship.iterator(); it.hasNext();) {
Container container = it.next();
if (container.contains(good) {
it.remove();
}
}
}
}
}
I'm using inside an iterative algorithm an HashSet that is dynamically enlarged at each algorithm iteration by adding new objects (via method add). Very frequently I check if a generated object has been already put inside the HashSet by using the contains method. Observe that the HashSet may include several thousand objects.
Here follows a citation from the doc about class HashSet:
"This class offers constant time performance for the basic operations (add, remove, contains and size), assuming the hash function disperses the elements properly among the buckets."
Apart from other considerations provided inside the doc (not reported for simplicity), I see that add and contains are executed in constant time.
Please, can you suggest another data structure in Java that provides better performance for the "contains" operation with respect to my problem?
Classes from Apache Commons or Guava are also accepted.
The performance of HashSet.contains() will be as good as you can get provided your objects have a properly implemented hashCode() method. That will ensure proper distribution among the buckets.
See Best implementation for hashCode method
As other answers already stated "constant time" is the best runtime-behaviout you can get.
If you will get it does depend on your hashcode-implementation, but since you use the NetBeans suggestion you shouldn't be too bad there.
As to how to keep the "constant time" as small as possible:
try to allocate your HashSet large enough from the very beginning to avoid costly rehash-operations
You can cache your calculated hashcode the first time hashCode() is called and return the cached value later on. There should be no need to add some triggering-mechanism to clear the cache on object-updates, since your relevant fields should be immutable - if they aren't you are bound to run into trouble using HashSet anyway.
You can let your object remember if it has been put in that hashset. Just have a boolean field to store if it was added to the hash set. Then you don't need to call contains on the HashSet but just read the field value of your object. This method will only work if the object is put in exactly one hashset that will check the boolean field.
It might be extended to a constant number of hashsets using java.util.BitSet in the object contained in the hashset where every hashset can be identified by a unique integer when the number of hashsets is known before the algorithm starts.
Since you are saying that you are calling contains frequently, it makes sense to replace newly generated objects with equal existing objects (object pooling), since the overhead of that will amortize by having contains being only a single field read.
As requested here is some sample code. The special set implementation is about 4 times faster than a normal hash set on my machine. However the question is how well this code reflects your use case.
public class FastSetContains {
public static class SetContainedAwareObject {
private final int state;
private boolean contained;
public SetContainedAwareObject(int state) {
this.state = state;
}
public void markAsContained() {
contained = true;
}
public boolean isContained() {
return contained;
}
public void markAsRemoved() {
contained = false;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + state;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SetContainedAwareObject other = (SetContainedAwareObject) obj;
if (state != other.state)
return false;
return true;
}
}
public static class FastContainsSet extends
HashSet<SetContainedAwareObject> {
#Override
public boolean contains(Object o) {
SetContainedAwareObject obj = (SetContainedAwareObject) o;
if (obj.isContained()) {
return true;
}
return super.contains(o);
}
#Override
public boolean add(SetContainedAwareObject e) {
boolean add = super.add(e);
e.markAsContained();
return add;
}
#Override
public boolean addAll(Collection<? extends SetContainedAwareObject> c) {
boolean addAll = super.addAll(c);
for (SetContainedAwareObject o : c) {
o.markAsContained();
}
return addAll;
}
#Override
public boolean remove(Object o) {
boolean remove = super.remove(o);
((SetContainedAwareObject) o).markAsRemoved();
return remove;
}
#Override
public boolean removeAll(Collection<?> c) {
boolean removeAll = super.removeAll(c);
for (Object o : c) {
((SetContainedAwareObject) o).markAsRemoved();
}
return removeAll;
}
}
private static final Random random = new Random(1234L);
private static final int additionalObjectsPerIteration = 10;
private static final int iterations = 100000;
private static final int differentObjectCount = 100;
private static final int containsCountPerIteration = 50;
private static long nanosSpentForContains;
public static void main(String[] args) {
Map<SetContainedAwareObject, SetContainedAwareObject> objectPool = new HashMap<>();
// switch comment use different Set implementaiton
//Set<SetContainedAwareObject> set = new FastContainsSet();
Set<SetContainedAwareObject> set = new HashSet<>();
//warm up
for (int i = 0; i < 100; i++) {
addAdditionalObjects(objectPool, set);
callSetContainsForSomeObjects(set);
}
objectPool.clear();
set.clear();
nanosSpentForContains = 0L;
for (int i = 0; i < iterations; i++) {
addAdditionalObjects(objectPool, set);
callSetContainsForSomeObjects(set);
}
System.out.println("nanos spent for contains: " + nanosSpentForContains);
}
private static void callSetContainsForSomeObjects(
Set<SetContainedAwareObject> set) {
int containsCount = set.size() > containsCountPerIteration ? set.size()
: containsCountPerIteration;
int[] indexes = new int[containsCount];
for (int i = 0; i < containsCount; i++) {
indexes[i] = random.nextInt(set.size());
}
Object[] elements = set.toArray();
long start = System.nanoTime();
for (int index : indexes) {
set.contains(elements[index]);
}
long end = System.nanoTime();
nanosSpentForContains += (end - start);
}
private static void addAdditionalObjects(
Map<SetContainedAwareObject, SetContainedAwareObject> objectPool,
Set<SetContainedAwareObject> set) {
for (int i = 0; i < additionalObjectsPerIteration; i++) {
SetContainedAwareObject object = new SetContainedAwareObject(
random.nextInt(differentObjectCount));
SetContainedAwareObject pooled = objectPool.get(object);
if (pooled == null) {
objectPool.put(object, object);
pooled = object;
}
set.add(pooled);
}
}
}
Anothe Edit:
using the following as the Set.contains implementation makes it about 8 times faster than a normal hashset:
#Override
public boolean contains(Object o) {
SetContainedAwareObject obj = (SetContainedAwareObject) o;
return obj.isContained();
}
EDIT:
This technique has a bit with the class enhancement of OpenJPA in common. The enhancement of OpenJPA enables a class to track its persistent state which is used by the entity manager. The suggested method enables an object to track if itself is contained in a set which is used by the algorithm.
So I'm writing a program for an assignment where I store Patients into a TreeSet. The problemn I'm having is I have to implement a method to discharge a specefic patient from the TreeSet.
for(int i = 0; i < 10 ; i++){
Random ag = new Random();
int age = ag.nextInt(99) + 1;
Names randomname = Names.getRandom();
String name = randomname.name();
String sex;
if(Math.random() > 0.5)sex = "female";
else sex = "male";
Random sn = new Random();
int serial = sn.nextInt(10000) + 1;
Address randomAddress = Address.getRandom();
String address = randomAddress.name();
Hospital.admitPatient(new Patient(age, name, sex, serial, Birthday.produceBirthday(), address));
}
So Thats how I am looping to get the Patients info and stats for the Patient Object. The admit patient method adds them to the TreeSet.
public static void admitPatient(Patient obj){
if(numofPatients < maxPatients){
patientList1.add(obj);
}
}
The Problem I'm having is withbthe Discharge patient method. Where I don't know what to put in the method
public static void dischargePatient(What do i put here in the driver when i call this method?){
patientList1.remove(w/e i put up there);
}
Since I didn't name the Objects of patients when creating them but just inserted them straight into the TreeSet I'm not sure exactly how to call them when i call the discharge patient method.
As you usually want to work with selected objects (patients) and not the whole list, you need a way to identify them somehow (for example by name or ID).
Since add and remove are similar, your dischargePatient method will be similar as well. Try
public static void dischargePatient(Patient patient) {
patientList1.remove(patient);
}
To retrieve a patient with a certain ID, you may iterate through your set and return it:
public Patient getPatientByID(String id) {
for (Patient patient : patientList1) {
if (patient.getID().equals(id)) {
return patient;
}
}
}
To remove a patient with ID "1234abc", you could do the following:
dischargePatient(getPatientByID("1234abc"));
Using this pattern, you rebuild the functionality of the map datastructure. Thus it might be better to use a Map (e.g. HashMap<>). Code will be reduced to operations like:
Map<String, Patient> patients = new HashMap<>();
patients.put("1234abc", patient1);
patients.remove("1234abc");
Full code for your example:
public static void admitPatient(Patient patient) {
if(numofPatients < maxPatients){
patients.put(patient.getID(), patient);
}
}
public static void dischargePatient(String id) {
patients.remove(id);
}
I have a graph class with Node's, where each Node can connect to others:
public class Node {
List<Node> connections;
}
I would like to make a deep copy of the entire graph. As a first attempt, I tried making a copy constructor like:
public Node(Node other) {
connections = new ArrayList<Node>();
for (Node n : other.connections) {
connections.add(new Node(n));
}
}
So deep copying a graph would just be:
public Graph deepCopy () {
Graph g = new Graph();
g.nodes = new ArrayList<Node>();
for (Node n : nodes) {
g.nodes.add(new Node(n));
}
}
But that doesn't work as that destroys the connection relationship among the nodes. I am wondering if anyone has suggestions to do this in a simple way? Thanks.
The problem is that you need to copy the identities of the nodes, not just their values. Specifically, when you're copying some node, you need to deal with the identities of the nodes it refers to; that means that a copy constructor, or some other kind of purely local copying mechanism, can't do the job, because it only deals with one node at a time. I'm not sure that makes any sense, but I've typed it and my backspace key doesn't work.
Anyway, what you can do is pass around some other object which can tell which new node corresponds to which old node. If you wanted to be fancy (and who doesn't?) you could refer to this as a graph isomorphism. This can be something as simple as a map. As in this completely untested code:
// in Graph
public Graph deepCopy () {
Graph g = new Graph();
g.nodes = new ArrayList<Node>();
Map<Node, Node> isomorphism = new IdentityHashMap<Node, Node>();
for (Node n : nodes) {
g.nodes.add(n.deepCopy(isomorphism));
}
return g;
}
// in Node
public Node deepCopy(Map<Node, Node> isomorphism) {
Node copy = isomorphism.get(this);
if (copy == null) {
copy = new Node();
isomorphism.put(this, copy);
for (Node connection: connections) {
copy.connections.add(connection.deepCopy(isomorphism));
}
}
return copy;
}
Sergii mentions using serialization; serialization actually does something pretty similar when it traverses an object graph.
Yep, deep copy in java ( not only in java) can be made using memory serialization/deserialization
like this
public static Object copy(Object orig) {
Object obj = null;
try {
// Write the object out to a byte array
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(orig);
out.flush();
out.close();
// Make an input stream from the byte array and read
// a copy of the object back in.
ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bos.toByteArray()));
obj = in.readObject();
}
catch(IOException e) {
e.printStackTrace();
}
catch(ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
return obj;
}
Kinda late input. But I had a similar problem but came to a different solution. But not shure if its bulletproof. So please feel free to comment so I can learn!
I have a Type called "Numbers" because I have no creativity naming stuff.
Each object of type "Numbers" has an internal list that can carry additional objects of type "Numbers" of which each has a list of additional "Numbers" of which each... and so on.
Basicaly you can make a tree structure similar to this:
I solved the deep copy problem by using a recursive copy-constructor inside the "Numbers" class.
Numbers class:
import java.util.ArrayList;
public class Numbers {
private ArrayList<Numbers> numbers = new ArrayList<>();
private int number;
public Numbers(int number) {
this.number = number;
}
public Numbers(Numbers numToCopy) {
this.number = numToCopy.getNumber();
ArrayList<Numbers> list = numToCopy.getNumbers();
for(int i = 0; i < list.size(); i++) {
Numbers n = new Numbers(list.get(i));
numbers.add(n);
}
}
public void addNumber(Numbers i) {
numbers.add(i);
}
public ArrayList<Numbers> getNumbers() {
return numbers;
}
public void setNumber(int i) {
this.number = i;
}
public int getNumber() {
return number;
}
public ArrayList<Numbers> getAllNumbers(ArrayList<Numbers> list) {
int size = numbers.size();
list.addAll(numbers);
for(int i = 0; i < size; i++) {
numbers.get(i).getAllNumbers(list);
}
return list;
}
}
Usage:
import java.util.ArrayList;
public class NumbersTest {
public NumbersTest() {
}
public static void main(String[] args) {
Numbers num0 = new Numbers(0);
Numbers num1 = new Numbers(1);
Numbers num2 = new Numbers(2);
Numbers num3 = new Numbers(3);
Numbers num4 = new Numbers(4);
Numbers num5 = new Numbers(5);
Numbers num6 = new Numbers(6);
num0.addNumber(num1);
num0.addNumber(num2);
num1.addNumber(num3);
num1.addNumber(num4);
num2.addNumber(num5);
num2.addNumber(num6);
num4.addNumber(num6);
//Deep copy here!
Numbers numCopy = new Numbers(num0);
//Change deep down in graph of original
num0.getNumbers().get(0).getNumbers().get(1).getNumbers().get(0).setNumber(799);
//Printout of copy to show it was NOT affected by change in original.
for(Numbers n : numCopy.getAllNumbers(new ArrayList<Numbers>())) {
System.out.println(n.getNumber());
}
}
}
Usage code shows that changing deep inside the "graph" of the original num0 object, does not change the copy made of it.
Theres two sixes (6) in the graph, and thats ok since they are on different branches.
Downside is if same number would repeat through one of the paths, like if there was a (1) somewhere under the first 1. It would then end up in an infinite loop.
Please do comment! :)