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);
Related
I am trying to add value for the List which is stored in HashMap and that has one parent List.
When I try to do so I get "The method get in type is not compatible with the List"
I am trying the following code, logic is :
If I get the matching value of tID in the txnValue List I am just adding the "Values" List otherwise I am creating the new HashMap.
List < HashMap > txnvalues = new ArrayList < HashMap > ();
for (LinkedHashMap < String, Object > linkedHashMap: resultset) {
HashMap data = new HashMap < > ();
HashMap attrData = new HashMap < > ();
List values = new ArrayList < > ();
data.put("values", new ArrayList < > ());
attrData.put("attrID", linkedHashMap.get("ID"));
attrData.put("attrVal", linkedHashMap.get("VAL"));
String txnID = linkedHashMap.get("T_ID").toString();
if (!txnvalues.stream().anyMatch(list -> list.containsValue(txnID))) {
data.put("tID", linkedHashMap.get("T_ID"));
values.add(attrData);
data.put("Values", values);
txnvalues.add(data);
} else {
txnvalues.get("Values").add(attrData); // this Line throws error
}
}
Example :
[{
"tID":123,
"Values":[{attrID:1,attrVal:123}]
}]
//Here If linkedHashmap.get("T_ID") = 123 which matches with tID then I want to add data in the Values
[{
"tID":123,
"Values":[{attrID:1,attrVal:123},{attrID:11,attrVal:467}]
}]
//If it doesn't match then I want to create new Hashmap and update txnValues Like this
[{
"tID":123,
"Values":[{attrID:1,attrVal:123},{attrID:2,attrVal:3435}]
},
{
"tID":456,
"Values":[{attrID:2,attrVal:233}]
}
]
I decided to parameterize all of your various iterables. Below is the parameterized code.
List<HashMap<String, List<HashMap<String, Object>>>> txnvalues = new ArrayList<HashMap<String, List<HashMap<String, Object>>>>();
for (LinkedHashMap<String, Object> linkedHashMap : resultset) {//Error here
HashMap<String, List<HashMap<String, Object>>> data = new HashMap<String, List<HashMap<String, Object>>>();
HashMap<String, Object> attrData = new HashMap<String, Object>();
List<HashMap<String, Object>> values = new ArrayList<HashMap<String, Object>>();
data.put("values", new ArrayList<>());
attrData.put("attrID", linkedHashMap.get("ID"));
attrData.put("attrVal", linkedHashMap.get("VAL"));
String txnID = linkedHashMap.get("T_ID").toString();
if (!txnvalues.stream().anyMatch(list -> list.containsValue(txnID))) {
data.put("tID", linkedHashMap.get("T_ID")); //Error here
values.add(attrData);
data.put("Values", values);
txnvalues.add(data);
} else {
txnvalues.get("Values").add(attrData); //Error here
}
}
First, you have multiple errors in your code such as trying to put a String key and Object value into data, which is a HashMap that only takes a String key and a List(of HashMaps of Strings and Objects) value. Another such is trying to get an item from txnvalues by a String, when txnvalues is a List and therefore requires an integer index parameter.
Second, you have a variable here which is never defined: resultset. We don't know what it is or how it is used, since it's never referenced elsewhere.
Third, there are many many ways to handle nested sets. This >-> List<HashMap<String, List<HashMap<String, Object>>>> is simply horrible.
Please re-write your code in a way that is readable, parameterized, and can properly compile without errors. Just parameterizing will help you keep track of which iterables take which parameters and will help prevent the problem you had when you came here for help.
I'm probably late with this answer. Nevertheless, I'll introduce a possible remedy accompanied by a detailed explanation.
At the first glance, such a deeply nested collection seems contrived and incomprehensible. But problems that you can see in this code aren't something unusual, they could be observed in many questions on StackOverflow, and in many repositories. The only difference is in concentration.
Let's try to examine it closely. A map is a data structure that is commonly misused by beginners because it allows to combine objects of different nature. I am pretty sure that provided code models something more or less tangible. Did you notice that PO tries to access an entry that has a string key called "id"? That's a clear indicator that collections here are used in place of objects.
If I say object graph can be far more complex, it probably wouldn't be something new. But how to reason about the code that is written in such a way?
Let's step aside for a moment and consider the following task:
there are a number of sailboats, you need to determine which of them will win the race and return its name as a result;
input provided as a plain text and consists of the following parameters: unique name, displacement, and weight (only these three for simplicity);
the speed of the vessel depends on its displacement and weight (i.e. formula is provided, we need only parse the values);
It is very likely that somebody can come up with such a solution:
create a Map<String, List<Double>>, where the key is a sailboat's name and the value is a list that contains displacement and weight;
then just iterate over the entry set, apply the formula and so find the fastest vessel.
Only a couple of methods, and it seems that a separate class for a sailboat will allegedly increase the overall complexity and amount of code. That's a common delusion for many students. The creation of a separate class will provide a logical structure to the code and will pay off if you would wish to extend or reuse it. Note that not only attributes of the sailboat must belong to this class but also the methods that allow to compute sailboat's speed and compare sailboats based on it.
Decomposition is a skill and it has to be exercised. And for those of you who didn't realize from the beginning that a sailboat in the previous example has to be represented by an object, I advise to try the next exercise: describe a university, a candy shop, a grocery store, a cat, anything you like but without using objects. First, think about a couple of use-cases that entail accessing some properties of the elements of the system that you're trying to model. Then draw diagrams and write the code using warriors collections and arrays, pay attention that the more complex your system becomes, the more cumbersome become all nested maps and lists, which make you write your code like this:
map.get(something).get(something).add(somethingElse);
And then, when you see the problems, you are ready to implement the classes that make sense in your domain model and compare the two approaches.
Disclaimer: understanding decomposition is a crucial thing but class design is a very broad topic, there are lots of things to study in this area like classic principles and design patterns. But before diving into these topics, you have to have a firm understanding of decomposition and OOP. Without this knowledge even with an object-oriented approach, your solution could become convoluted and difficult to manage. But this is a step in the right direction. The fact alone that you are using an object-oriented language doesn't automatically make your solution object-oriented. It's a skill, and it has to be exercised.
It was a very long digression, now let's get to the point.
As I already said, I'm convinced that the post author had in mind some kind of natural use case. Instead of names that describe the system in this maze of data structures we can see only dump get() and put(). But there's a clue in the usage of map. An id as a key is a clear indicator that it has to be an object which is substituted by a map.
That is a start of a journey, I'll try to provide a scenario that makes sense (at least a bit) and pieces of a system that fits into a structure depicted in the scheme provided at the start of this post.
Let's consider an organization that sells something (I'm not trying to guess what was the author's intention, but providing a use case that will allow to reason about the code). There are a bunch of departments, each with a unique identifier.
Each department has a collection of products that it sells. Department gets different products from different suppliers. And in turn, each product has a unique id a collection of suppliers represented by plain string (it looks contrived, but keep in mind it's just an illustration of what the code does).
As a use-case, let's assume that the company launches a new product and it must be accessible in all its departments. The code checks whether the department has this product already, if not, the product will be added with a default set of suppliers, otherwise it merges the existing set of suppliers and the default one.
As you can see the code in the main method is very concise. Note that all the miscellanies of data structures are still there, but we are not accessing them directly. As the information expert principle suggests, this logic is hidden inside the objects. That makes this solution reusable and less error-prone.
public static void main(String[] args) {
// this list is a rough equivalent of the "List<Map<String, List<Map<String, Object>>>> txnvalues"
List<Department> departments =
List.of(new Department("dep11"), new Department("dep12"));
Product newProd = new Product("id123"); // a NEW Product with id = "id123"
newProd.addAllSuppliers(List.of("supplierA", "supplierB"));
for (Department dep: departments) { // launching the new Product
dep.mergeProduct(newProd);
}
}
public class Department {
private final String departmentId;
private final Map<String, Product> idToProduct;
public Department(String departmentName) {
this.departmentId = departmentName;
this.idToProduct = new HashMap<>();
}
public void mergeProduct(Product prod) {
idToProduct.merge(prod.getId(), prod, Product::merge);
}
public void mergeAllProducts(Iterable<Product> products) {
for (Product prod: products) {
mergeProduct(prod);
}
}
public void addProduct(Product prod) {
idToProduct.put(prod.getId(), prod);
}
public void addAllProducts(Iterable<Product> products) {
for (Product prod: products) {
addProduct(prod);
}
}
public String getId() {
return departmentId;
}
public Map<String, Product> getIdToProduct() {
return Collections.unmodifiableMap(idToProduct);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof Department other) {
return departmentId.equals(other.departmentId);
} else return false;
}
#Override
public int hashCode() {
return Objects.hash(departmentId);
}
}
public class Product {
private final String productId;
private final Set<String> suppliers;
public Product(String id) {
this.productId = id;
this.suppliers = new HashSet<>();
}
public boolean addSupplier(String newSup) {
return suppliers.add(newSup);
}
public boolean addAllSuppliers(Collection<String> newSup) {
return suppliers.addAll(newSup);
}
public Product merge(Product other) {
if (!this.equals(other)) throw new IllegalArgumentException();
Product merged = new Product(productId);
merged.addAllSuppliers(this.suppliers);
merged.addAllSuppliers(other.suppliers);
return merged;
}
public String getId() {
return productId;
}
public Set<String> getSuppliers() {
return Collections.unmodifiableSet(suppliers);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof Product other) {
return this.productId.equals(other.productId);
} else return false;
}
#Override
public int hashCode() {
return Objects.hash(productId);
}
}
Further steps:
First of all make sure that you don't have gaps in the core concepts of OOP: encapsulation, inheritance, and polymorphism.
Draw before you start to code, it's not necessary to create a full-blown UML diagram. Even a rough set of named boxes with arrows will help you understand better how your system is structured and how its parts interact with each other.
Read and apply. Extend your knowledge gradually and try to apply it. High cohesion, Low coupling, SOLID, and lots of helpful reading can be found here, for instance this recent post
Write a bit, test a bit: don't wait until your code became a beast. Write a bit and give it a try, add something else and take a look at how these parts fit together.
In the else block, you call get method of txnvalues which a list of HashMaps and thus it expects an integer index. I believe you assume that at this point you've got a reference to the HashMap to which you would add the values. But you don't.
So, you need to find the index where to add the values, which means you have to look through the txnvalues list again.
For this reason, you should use a different approach:
txnvalues.stream()
.filter(m -> m.get("tID").equals(txnID))
.findFirst()
.ifPresentOrElse(
m -> m.get("Values").add(attrData),
() -> {
HashMap data = new HashMap<>();
// Other stuff to fill the data
txnvalues.add(data);
}
);
Here .filter(m -> m.get("tID").equals(txnID)) corresponds to your .anyMatch(list -> list.containsValue(txnID)) (the parameter list is actually instance of HashMap).
I changed the condition: according to your data sample, you looking for Map which has txnID value for the "tID" key, therefore getting the value of this key is faster than looking through all the values in the HashMap. (It may return null.)
So filter will return only the entries which contain match the required value of the "tID" key. Then .findFirst() “returns” the reference to that HashMap. Now .ifPresentOrElse performs the actions you want:
m.get("Values").add(attrData) into the list; this corresponds your one line of code in the else block;
the other code is what you had in the if block: if nothing is found, create the new instance.
This subclass should be able to let the user choose a specific employee ID, type it into the command line and choose to either add it to the array list, delete it from the array list or simply request to see more information about the specific employee ID. I've tried so many things with no luck at all.
package WorkIDServerStorage;
public class EmployeeList{
private Employee[] theEmployee;
private int arrayEmployee;
public EmployeeList(){
theEmployee = new Employee[100];
arrayEmployee = 0;
}
public EmployeeList(int arraySize){
theEmployee = new Employee[arraySize];
arrayEmployee = 0;
}
public void setTheEmployee(Employee[] inputTheEmployee){
theEmployee = inputTheEmployee;
}
public void setArrayEmployee(int inputArrayEmployee){
arrayEmployee = inputArrayEmployee;
}
public Employee[] getTheEmployee(){
return theEmployee;
}
public int getArrayEmployee(){
return arrayEmployee;
}
public Employee addEmployeeID(Employee employeeAdd){
return theEmployee[arrayEmployee++] = employeeAdd;
}
public Employee deleteEmployeeID(int employeeDelete){
//Delete an employee record with a
//specified record number from the array
}
public Employee readEmployeeInfo(int employeeRead){
//Read the employee data for a specified record number
//From the array and display this data to the screen
}
#Override
public String toString(){
StringBuilder sb = new StringBuilder();
for(int x = 0; x < arrayEmployee; x++){
sb.append(theEmployee[x].toString()).append("\n");
}return sb.toString();
}
}
Arrays are fixed length data structures. They are much like a multi-storied building. You can't take a floor out from the middle of the building and expect it to stand.
ArrayList (as others have pointed out), are dynamic structures, much like a train. You can take out compartments as you wish, you can reconfigure it.
I would in fact recommend NOT using a List at all, but a Map that maps an ID to an employee record. Let us say you have the following employees in a list -
Alice
Bob
John
Ruth
If you delete "Bob" the ID's for John and Ruth are going to change. Not a good practice. If you use a Map instead, everyone can keep their assigned ID's and you just add to the map by incrementing the keys (or IDs).
Hope this helps.
For being able to dynamically adding or removing from an array, you should use List, or ArrayList. Typical arrays don't provide delete or add at runtime, since they are fixed-size and if you want to control the procedure by handling the indices or other tricks, you'll probably end up in a messy and hard to maintain code.
On the other hand, Lists in java are dynamically sized and provide add(), remove(), get() and other convenient methods, which in my opinion best suits you.
You should use an ArrayList in order to dynamically add and remove entries in your array.
Make your theEmployee array into an ArrayList by doing:
ArrayList<Employee> theEmployee = new ArrayList<Employee>();
You can add and remove by doing:
public boolean addEmployeeID(Employee employeeAdd){
return theEmployee.add(employeeAdd);
}
public Employee deleteEmployeeID(int employeeDelete){
return theEmployee.remove(employeeDelete)
}
I changed the return type of addEmployeeID to boolean because an ArrayList returns a boolean when you add an object.
For remove, int employeeDelete would be the index of the object in the array. When you remove an object, all the remaining objects get shifted to the left. So if you have an array [1,2,3] and you remove 2, it would become [1,3]. The indexes are moved.
You could also remove an object so this should work:
public boolean deleteEmployeeID(Employee employeeDelete){
return theEmployee.remove(employeeDelete)
}
Where employeeDelete is an Employee and the function returns a boolean.
In a small project I am working on I've gotten stuck. The user enters a command that may be "xp Speed", my command handler class finds that it wants to the XP value of the Speed Instance. In this case it needs to return the value of Skill.Speed.currentXP back to the user.
Small Part of the program:
//Example Instance initialization there is over 40 of these
Skill Speed = (new SkillSpeed(Skills.SKILL_SPEED,Skills.SKILL_SPEED_MODIFIER));
//Constructor for skill class
public Skill(String skillName, double modifier) {
this.name = skillName;
this.minLevel = Skills.MIN_SKILL_LEVEL;
this.Modifier = 1f;
this.currentLevel = (int)calculateLevel();
this.currentXP = 1;
this.leaderboard = getCurrentLeaderboard();
this.ID = getNextID();
}
Now, theres one way i could do this. by having a switch statement with case value being the string entered. However I'm sure having 40+ cases in one switch statement must be avoidable. The other theory I have had is creating a array of all current instances then iterating through that list, finding if the user inputted string is equal to the name of that instance, then returning the instance itself. This is what I came up with:
//method inside another classs that attempts to return the appropriate skill Instance
public Skill getSkillFromName(String Name) {
for(int i = 0; i < Skill.SkillArray.length; i++) {
final String SkillName = Skill.SkillArray[i].getName();
if(SkillName.equalsIgnoreCase(Name)) {
return Skill.SkillArray[i];
}
}
return null;
}
So here's what I need help with:
Creating a array of all initialized instances
Creating the method that will return Skill."InsertRandomInstanceDependingOnUserInputHere".currentXP
Fixing any problems you see in the getSkillFromName() method
Or perhaps I have overlooked a far easier way of doing this, and you can help me with that.
Thanks for the help,
BigDaveNz
If the names of the skills excatly match method names you might find the aswer at "How do I invoke a Java method when given the method name as a string?".
For finding instances by name you can still use Map's.
You can use a Map for this. E.g.:
Map<String, Skill> skills = new HashMap<String, Skill>();
To insert the values you put the values into the Map:
skills.put(skill.getName(), skill);
To retrieve your skill you can get the skill by name:
Skill skill = skills.get(name);
This is more of a design question with implications for code simplicity vs. performance.
Lets say you want to make sure a set of values for a given user id are the same between two systems. The example here is to check that a student id has the same number of course enrollments in System A and System B.
For this we create:
List<String> studentList = new ArrayList<String>();
Set<String> sysAEnrollments = new HashSet<String>();
Set<String> sysBEnrollments = new HashSet<String>();
private Map<String, String> badEnrollList = new HashMap<String, String>();
And fill them appropriately, given a list of student ids(studentList):
studentList = getCurrentStudentList();
for (String id : studentList){
sysAEnrollments = getSysAEnrollments(id);
sysBEnrollments = getSysBEnrollments(id);
if (!sysAEnrollments.containsAll(sysBEnrollments)){
badEnrollList.put(id, getBadEnrollmentsById(id, sysAEnrollments, sysBEnrollments));
}
}
Question: What should the method 'getBadEnrollmentsById' return?
Either a concatenated string with enough meaning so it can just be printed out.
Or have a new object, for example another collection with the list of course ids that could be used for further processing but harder to use for printed output.
Is it worth designing thoroughly all expected objects or replace some of them with concatenated strings for clarity and performance?
NOTES:
System A is preferred as the authoritative source
Output from getBadEnrollmentsById should have all courses and flag those missing in system B.
PROPOSED SOLUTION: (2012-SEP-14)
EDIT (2012-SEP-17): Updated the Course class to include hashCode and equals
As suggested by user351721 I continued modelling the remaining objects that match the expected results/requirements.
Slight changes made a big difference and allowed me to go over this design flaw and finish with the implementation.
The revised collections are:
List<String> studentList = new ArrayList<String>();
Enrollment sysAEnrollments;
Enrollment sysBEnrollments;
Map<String, List<String>> badEnrollList = new HashMap<String, List<String>>();
And we populate the Enrollments:
for (String id : studentList){
sysAEnrollments = getSysAEnrollments(id);
sysBEnrollments = getSysBEnrollments(id);
if (!sysAEnrollments.getCourses().containsAll(sysBEnrollments.getCourses())){
List<String> missingCourses = getProblemEnrollmentListById(id, sysAEnrollments, sysBEnrollments);
badEnrollList.put(id, missingCourses);
}
}
So for now the output can be printed from badEnrollList by getting at each ArrayList and printing the course names. A course name with a * will mean that it's missing in sysB.
The Enrollment class looks like this:
public class Enrollment {
private Set<Course> courses = new HashSet<Course>();
public void setCourses(Set<Course> courses){
this.courses = courses;
}
public Set<Course> getCourses(){
return this.courses;
}
}
And the Course class ended up like this:
public class Course {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(final String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
// Must override hashCode() and equals()
#Override
public boolean equals(Object o){
if (o == this)
return true;
if (!(o instanceof Course))
return false;
Course c = (Course) o;
return c.id.equals(this.id) && c.name.equals(this.name);
}
#Override
public int hashCode(){
// Magic numbers as shown on Joshua Bloch's book "Effective Java" 2nd Edition, p.48
int result = 17;
result = 31 * this.id.hashCode();
result = 31 * this.name.hashCode();
return result;
}
}
The changes might look subtle but the important clue is that Enrollments are not a collection of strings, Enrollments are a collection of Courses AND each Course has a name and a availability property. They don't seem to do much but by using them I am defining the objects that I'm working with and documenting how these classes can be reused in the future.
"Growing Object-Oriented Software, Guided by Tests" addresses this question: chapter 7, "Value Types". Worth reading. An excerpt:
The more code we write, the more we’re convinced that we should define types to represent value concepts in the domain, even if they don’t do much. It helps to create a consistent domain model that is more self-explanatory. If we create, for example, an Item type in a system, instead of just using String, we can f ind all the code that’s relevant for a change without having to chase through the method calls
concatenated strings
would mean you have to define a pattern and corresponding set of valid strings and implement validation and translation to entity classes. Providing an interface or class would make it easier to update your code in a year or so, not to mention other programmers that might work with your application. Why not store student, enrollment or course objects in badEnrollList? How do these objects look like and what do you want to do with them?
In general: Yes, designing thoroughly all expected objects is worth it.
I feel that a collection, such as List<String> would be a desirable return value. This allows you to more efficiently capture multiple discrepancies between the two sets, and process the missing courses in your second object more intuitively. Printing the list wouldn't be that hard, either - depending on how you wished to convey the information.
It's also worth mentioning that the .equals() method for Set is a cleaner and more intuitive way to ensure equivalence between two sets.
Instead of using all these sets and maps, I'd use Plain Old Java Objects (POJOs) that reflect the actual business objects in question. From what you've indicated, you have Students who have an id of some sort, and who are enrolled in classes on System A and on System B. I would build up a set of Student objects defined like so:
public class Student {
private String id;
private List<String> enrollmentsA;
private List<String> enrollmentsB;
// appropriate getters and setters
}
Depending on if you want to do anything else with Classes, it may even be preferable to create some form of EnrolledClass object to represent that too.
Within the students class, I'd then have a method that would determine the "bad" enrollments. If all that you want to do with this data is generate an email message, it may even be as simple as a String:
public String getBadEnrollmentsMessage() {
List<String> enrolledBoth = getCommonEnrollments();
List<String> enrolledOnlyA = getAOnlyEnrollments();
List<String> enrolledOnlyB = getBOnlyEnrollments();
StringBuilder output;
// format the contents of the above lists into output
// format should be however you want it in the email.
return output.toString();
}
Then you could have a map of Students to email enrollments messages:
HashMap<Student, String> studentEmails;
for (Student s : allStudents) {
studentEmails.put(s, s.getBadEnrollmentsMessage());
}
Of course, if you have a method like getBadEnrollmentsMessage(), I'm not even sure you need the Map of students and strings in the first place. Frankly you could just create a sendEnrollmentEmail method, pass in a Student, and extract the message via getBadEnrollmentsMessage() right there.
I'm trying to select and get values from customer objects. What I want to do is to enter a personal number like "702312" and search after the customer objects that has a data member personal number that are equal to "702312". And when I found it I want to get the rest of the values or change it's content. This is some code that creates the customer objects from the class Customer and then it's stored in a arraylist.
// create a new customer object and send personal number, name and account number to constructor
Customer customer = new Customer(personalNumber, name, newAccountNumber);
// create an arraylist to store customer objects
ArrayList<Customer> customerList = new ArrayList<Customer>();
// add the new customer object to arraylist that holds all customer objects
customerList.add(customer);
I have tried to reach the values like this, but it's not working, so I'm looking for some help?
// search for customer
for (Customer customer : customerList) {
if(customer.getAccountOwnerPersonalNumber() == "702312"){
System.out.println("OK!!!");
}
}
And instead of:
if(customer.getAccountOwnerPersonalNumber() == "702312")...
I have tried this:
if(customer.personalNumber == "702312")...
Finally I have also tested it like this:
for(int i=0;i<customerList.size();i++){
if(customerList.get(i).getAccountOwnerName() == "702312");
System.out.println("OK");
break;
}
I'm not sure if I'm doing right!? Help is preciated! Thanks!
You need to use the equals() method to compare objects like Strings by their internal value (otherwise they will only be compared by reference):
if (customer.getAccountOwnerPersonalNumber().equals("702312")) {
System.out.println("OK!!!");
}
or, better, if it can potentially return null:
if ("702312".equals(customer.getAccountOwnerPersonalNumber())) {
System.out.println("OK!!!");
}
or, if applicable, just make it a primitive like int, so that == will work the way you intended:
private int accountOwnerPersonalNumber;
with
if (customer.getAccountOwnerPersonalNumber() == 702312) {
System.out.println("OK!!!");
}
You want .equals():
if(customerList.get(i).getAccountOwnerName().equals("702312"))
customer.getAccountOwnerPersonalNumber() == "702312" this is wrong you cant compare them like that because it will compare their object type. you should do like this
if(customer.getAccountOwnerPersonalNumber().equals("702312"))
or this is better
if("702312".equals(customer.getAccountOwnerPersonalNumber()))
If the data that you query for is a String then in that case you have to use
equals()
So the entire things will turn out to be
if(customer.getAccountOwnerPersonalNumber().equals("702312")) {
System.out.println("OK!!!"); }
You're trying to compare customer.personalNumber to "702312" which is a String, so i conclude customer.personalNumber is a String too.
If I'm right, try String.equals() method instead of == operator. This is because this operator compares objects instead of value, which is what you want.
if(customer.getAccountOwnerPersonalNumber().equals("70312");)
...
I would suggest you use java.util.Collections binarySearch(...) method. It's of order O(log n)(in this case, since ArrayList implements RandomAccess) which is tons better than the O(n) order you get from doing your for loop.
You can make a separate comparator for the desired Customer fields(s) (in this case "personalNumber"):
private static class CustomerPersonalNumberComparator implements Comparator<Customer> {
#Override
public int compare(Customer o1, Customer o2) {
return o1.getPersonaNumber().compareTo(o2.getPersonaNumber());
}
}
Also, don't forget to sort your list according to your desired search criteria (the Comparator) before using binary search on it:
Collections.sort(customerList,new CustomerPersonalNumberComparator());
int desiredIndex = Collections.binarySearch(customerList, new Customer("658",null,null), new CustomerPersonalNumberComparator());
Customer desiredCustomer = desiredIndex == -1 ? null : customerList.get(desiredIndex);
And don't forget that the binary search returns the position inside our array for the desired element or -1 if nothing was found. So you must use that returned position to retrieve the desired instance from the list.