I have a Java class as
class Students{
private String fName;
private String lName;
private String uName;
public Students(String fName, String lName, String uName) {
this.fName = fName;
this.lName = lName;
this.uName = uName;
}
public String getuName() {
return uName;
}
public void setuName(String uName) {
this.uName = uName;
}
public String getfName() {
return fName;
}
public void setfName(String fName) {
this.fName = fName;
}
public String getlName() {
return lName;
}
public void setlName(String lName) {
this.lName = lName;
}
}
Also I call this using
public class TestClass {
public static void main(String[] args) {
Students students1 = new Students("xyz","abc","xyzAbc");
Students students2 = new Students("poi","son","poison");
Students students3 = new Students("yog","jos","yogjos");
Students students4 = new Students("xyz","abc","xyzAbc");
Students students5 = new Students("pon","son","xyzAbc");
Students students6 = new Students("yog","jos","someotherUName");
Students students7 = new Students("yog","jos","someotherUName2");
List studentList1 = new ArrayList();
List studentList2 = new ArrayList();
studentList1.add(students1);
studentList1.add(students2);
studentList1.add(students3);
studentList2.add(students4);
studentList2.add(students5);
studentList2.add(students6);
}
}
Now I want a filtered list which would contain only unique "uName" values. Thus I want the comparison between "uName" field of each list and remove common ones.
At the end I would want 2 filtered list for studentList1 and studentList2.
I read about the removeAll method, but it seems to work with List of Integer/String data and not with List of Objects (as in my case).
You can put your list to set and then (if you need) to take it back:
new ArrayList(new HashSet<String>(list)) creates list that contains only unique elements from source list.
1. If you want each list to have unique uName value, then you can use TreeSet from java.util.Collection along with Interface Comparator from java.util.Comparator.
2. If you want to merge both the list and have unique uName, then combine both the list and then use TreeSet and Comparator.
3. Comparator gives the flexibility to compare in more than one way...
You can still use removeAll if the Objects in the List implement equals() properly.
AbstractCollection, which is the base for most kind of List implementations (including ArrayList) uses contains() in its implementation of removeAll. ArrayList's implementation of contains relies on indexOf(), which lastly uses equals().
You could implement equals() in your Student class to specify that an Student is equal to another if and only their uName fields are equal.
Please note that equals has associated semantics (see its javadoc), and you should be careful when choosing how to implement it. Consider if two student instances really represent the same student when their uNames are equal. In my opinion, this sounds like a very specific requirement of how to sort these things out and should not impact the semantics of the class.
You'll be much better off with #AlexR or #KumarVivekMitra's approach.
Firstly, you should be typing your lists:
List<Students> studentList1 = new ArrayList<Students>();
Secondly, implement hashCode() and equals() on your Students class that both delegate to uName:
public boolean equals(Object o) {
return o instanceof Students && ((Students)o).uName.equals(uName);
}
public int hashCode() {
return uName.hashCode();
}
Now removeAll() will work just fine.
Another option is to use Set, which only allows unique values as determined by the equals() method. If you add the above methods to your class, you could just do this:
Set<Students> students = new HashSet<Students>();
then add what you like to it and there will only ever be unique uName students in it.
btw, you should name your class in the singular - ie Student not Students.
You could also try using the apache commons CollectionUtils.disjunction. (link: http://commons.apache.org/collections/apidocs/org/apache/commons/collections/CollectionUtils.html#disjunction(java.util.Collection, java.util.Collection))
Your class Student should override methods hashcode and equals, so youy object can be unique by name. Here you can read how to do that.
Also note that all objects which are maintained in some collection should have those methods overriden, so you always know how are they compared.
In next step you can use removeAll method or the solution with Set, it's up to you :-)
Set example
List<String> list = new ArrayList(new HashSet<String>(studentList)); // here is your unique list
Or you could use custom Comparator instead of overriding hashcode and equals but if you want your students always unique, you should override them
A full working solution could be as follows.
Class Student{
.....
......
.....
public boolean equals(Object o) {
return o instanceof Students && ((Students)o).uName.equals(uName);
}
public int hashCode() {
return uName.hashCode();
}
}
Set studentList1 = new hashSet();
Set studentList2 = new hashSet();
Put your elements in these Sets.
Also if you want unique non-matching elements in both Sets. then write as follows.
studentList1.removeAll(studentList2 );
Related
I have an assignment for my class to sort a LinkedList that we made previously using the insert sort method. We created the List by reading in an excel file with 5 contributors listed. I realize this sounds like a repeat question...however, all of the samples I can find deal with Integers or arrays, nothing that I can find deals with strings or with a LinkedList like the one I am using. The other problem, the examples I do find that deal with more than just integers assume you made the list "from scratch", using Head and Node and stuff like that...as you can see in my code, I did not make mine from scratch, I just used the build in Java utility to make mine. Anyways, my code may not be super efficient, but I got a 100 on every assignment so far, so it's good enough for the school I guess, but any suggestions to make it better are welcome as well. I am a beginner to programming, only experience I have is previous classes. So, here is my code:
import java.io.*;
import java.util.*;
public class ChrisJohnson_Unit3_IP {
static class Contributor{ //create class to store contributor information
//declare variables
private String firstName;
private String lastName;
private String country;
private String phone;
private double contribution;
private int id;
//methods for setting variable values
public String getFirstName(){
return firstName;
}
public void setFirstName(String firstName){
this.firstName = firstName;
}
public String getLastName(){
return lastName;
}
public void setLastName(String lastName){
this.lastName = lastName;
}
public String getCountry(){
return country;
}
public void setCountry(String country){
this.country = country;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone){
this.phone = phone;
}
public double getContribution(){
return contribution;
}
public void setContribution(double contribution){
this.contribution = contribution;
}
public int getId(){
return id;
}
public void setId(int id){
this.id = id;
}
public void Print(){//method to print class objects
System.out.printf("%-10s %-10s %-8s %-15s %s %-15s %d %n", firstName, lastName, country,
phone, "$", contribution, id);
}
}//end Contributor class
static LinkedList contributorList = new LinkedList(); //create new Contributor Linked List
static Hashtable<String, Contributor> memberID = new Hashtable<>();//create new Hash Table
public static void main(String[] arg) throws Exception {
String response;
String ID;
Contributor contributorData = null;
Scanner in = new Scanner(System.in);
//print Welcome message and describe program to user
System.out.println("Welcome! This program will read your contributors.csv file "
+ "and store it into a list. \nTThe program will then sort the list and"
+ "print it for you to view/n");
System.out.println("Press enter to read the currently saved contributors.csv file...");
in.nextLine();
BufferedReader File =
new BufferedReader(new FileReader("contributors.csv"));
String dataRow = File.readLine(); // Read first line.
// The while checks to see if the data is null. If
// it is, end of file has been reached. If not,
// data will be processed.
while (dataRow != null){//While to read contributors.csv file and store in Contributor object
String[] data = dataRow.split(",");
contributorData = new Contributor(); //create new Contributor object
//store data into Contributor object
contributorData.setFirstName(data[0]);
contributorData.setLastName(data[1]);
contributorData.setCountry(data[2]);
contributorData.setPhone(data[3]);
contributorData.setContribution(Double.parseDouble(data[4]));
contributorData.setId(Integer.parseInt(data[5]));
ID = Integer.toString(contributorData.getId());
contributorList.push(contributorData);//add object to top of contributorList
memberID.put(ID,contributorData);//add contributor ID to key element of Hash Table
dataRow = File.readLine(); // Read next line of data.
}//end While to read contributors.csv file
File.close();//close CSV file
System.out.println("Here is your unsorted contributor list:\n");
//call Print method to print the list
System.out.printf("%-10s %-10s %-8s %-15s %-17s %s %n", "First", "Last",
"Country", "Phone #", "Contribution", "ID");
Iterator<Contributor> iter = contributorList.iterator();
while(iter.hasNext()){
iter.next().Print();
}//end while
System.out.println("Thank you for using this program!");
} //main()
}//end ChrisJohnson_Unit3_IP class
Again, the List has to be sorted by Name using the insert sort method. I understand the basic concept of the sort method, but honestly have no clue how to implement it here. I'm not looking for someone to do my homework for me, just give me a push in the right direction. Any help would be greatly appreciated, if you need anymore information please let me know. This assignment is due Monday, so hopefully someone is able to help me out by then. And yes, I have already written my instructor asking for help, I have been out of town all week so I have been trying to play catchup. Thank you for taking the time to read my question
First of all, I should say that doing an insertion sort on a linked list is totally pointless. Secondly, you could do something like the following, if you add a getName method that concatenates first and last names of the contributor (you can concatanate while sorting, but your code will be messier).
for( int i = 1; i < contributorList.size(); i++)
{
int j = i;
Contributor tmp;
while( j > 0 && contributorList.get(j-1).getName().compareTo( contributorList.get(j).getName()) > 0)
{
tmp = contributorList.remove( j);
contributorList.add( j-1, tmp);
j = j - 1;
}
}
First change your contributorList to use the Generic type of the objects it holds. That is LinkedList<Contributor>;. Second change the Object to implement Comparable. That is class Contributor implements Comparable<Contributor> and implement method public int compareTo(Contributor other). Third, pick a sorting method and implement it using compareTo to compare the objects for sorting.
Use ListIterator to find the correct point to insert the element and to the insertion. This allows you to do the insertion sort more efficiently than with the "standard approach", which would run in O(n³), since get and set runs in O(i) for index i. (The inner loop would run in O(i²), since O(1+2+...+i) = O(i²) and O(1²+2²+...+n²) = O(n³)).
Note that using a Iterator is enough to find the insertion point in O(n) and achieve O(n²) running time, but using ListIterator allows you to find and insert the element as well as remove the element for the next iteration of the outer loop iteration only a single iterator, if used in a clever way.
Using a Comparator to compare objects by a specified criterion also allows you to specify a criterion to sort by:
return value of comparator.compare(a, b) | meaning
-----------------------------------------------------------
0 | a == b
> 0 | a > b
< 0 | a < b
In java 8 you can easily create a Comparator given the method reference to a method returning the sort criterion given a object:
Comparator<Contributor> comparator = Comparator.comparing(Contributor::getLastName);
Without using method references this can be done using compareTo of a object implementing the Comparable interface, like String:
Comparator<Contributor> comparator = new Comparator<Contributor>() {
#Override
public int compare(Contributor o1, Contributor o2) {
return o1.getLastName().compareTo(o2.getLastName());
}
};
This way you use the Strategy Pattern for the ordering relation allowing you to use different sortings by passing different Comparators. And it's also the way the Collections class allows you to sort Lists with arbitrary contents, see Collections.sort(List, Comparator).
I have situation where I have a list(required items) that holds a table column result like:
NAME
ADDRESS
AGE
.
.
etc
In my method I get a User object that contains values for user.getName(), user.getAge() etc. I want to know the best way to ensure that every item in the list is present in the user object. The no of items in the list are variable.
public boolean isUserInfoComplete(User user, ArrayList list){
//so, if the list has AGE, the user.getAge() must have some value
}
One way I thought of is maintaining another list that holds values of every user info and checking that against my db list but that is not scalable.
It's not possible to dynamically match your method names with the list contents without reflection (which can be expensive and fragile). You may want to consider keeping your User values in a central Map cache. Here's one way to do that:
public class User {
private enum Field {
NAME,
AGE
//...
}
private Map<String, Object> values = new HashMap<>();
private void putValue(Field field, Object value) {
values.put(field.name(), value);
}
private Object getValue(Field field) {
return values.get(field.name());
}
public void setName(String name) {
putValue(Field.NAME, name);
}
public String getName() {
return (String)getValue(Field.NAME);
}
public void setAge(int age) {
putValue(Field.AGE, age);
}
public Integer getAge() {
return (Integer)getValue(Field.AGE);
}
//...
public boolean isUserInfoComplete(List<String> fields) {
return values.keySet().containsAll(fields);
}
}
You could use reflection to solve this problem if the items in the list match the getters in your User object.
For example, if AGE is in the list, you could use reflection to look for the getAge() method on the User class, call it on the object, and then check the result for null (or switch on the method return type to perform other types of checks).
Here's a starting point for you to experiment with (I haven't compiled or tested it):
public boolean isUserInfoComplete(User user, ArrayList list){
for(String attribute : list) {
String methodName = "get" + attribute.substring(0, 1).toUpperCase() + attribute.substring(1).toLowerCase();
Method method = User.class.getMethod(methodName, null);
if(method != null) {
Object result = method.invoke(user);
if(result == null) {
return false;
}
}
}
return true;
}
This seems like a case where you need reflection. This gives you the opportunity to inspect methods and field from your objects at runtime.
If you know your User-objects etc will follow a java bean standard then you will be able to use the getters for checking, though I see now problem in making your fields public final and checking directly on the fields themselves.
Take a look at https://docs.oracle.com/javase/tutorial/reflect/
You can check it using contains() while looping. This process will be very resource-consuming.
Maybe you can redesign something and simply compare two User objects? Will be faster. You can do it by providing your own implementation of equals and hashcode methods.
In an ArrayList, I have the same kind of objects. Each object has an id, name, and number as their fields. There is a chance that more than one object will have the same phone number. How can I make the ArrayList in such a way that all the ArrayList objects have distinct phone numbers?
override in your class methods equals() and hashCode(). In equals you will compare by phone number. Generate hashcode from your phone number too.
Now you are ready to use Set interface which will compare your objects by phone numbers automatically and exclude duplicates.
example below:
public class Test {
private int id;
private String name;
private String phoneNumber;
public Test(int id, String name, String phoneNumber) {
this.id = id;
this.name = name;
this.phoneNumber = phoneNumber;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Test test = (Test) o;
return phoneNumber.equals(test.phoneNumber);
}
#Override
public int hashCode() {
return phoneNumber.hashCode();
}
}
now instead of using List<Test> arr = ArrayList<Test>(), use Set<Test> mySet = new HashSet<Test>().
Try Set.
A collection that contains no duplicate elements.
First solution that comes to my mind is to use a HashMap.
Simply create a HashMap with the 'phonenumber' as key and your object as the value. After adding all the elements, you will have the list of objects with unique phone numbers. Simply iterate over this to create the List that you need.
I have a class Passengers which has member properties String name, int health, and String disease with setter and getter methods. The disease variable will initially hold null. Here's that class
public class Passengers
{
private String name;
private int health;
private String disease;
public Passengers(String _name, int _health, String _disease)
{
name = _name;
health = _health;
disease = _disease;
}
public void setHealth(int _health)
{
health = _health;
}
public void setDisease(String _disease)
{
disease = _disease;
}
public String getName()
{
return name;
}
public int getHealth()
{
return health;
}
public String getDisease()
{
return disease;
}
}
What I want to know is how I could add new strings onto this variable, and then how to take away. For example, a passenger Bill starts at null for his diseases, and then contracts malaria and the cold. Bill's disease variable should now hold malaria, cold. Now say the user chooses to treat Bill's malaria. How would I
1) add malaria and cold
2) subtract just malaria from disease?
Whenever I attempt to change the disease with
passengers[index].setDisease() = null;
it says "error: method setDisease in class Passengers cannot be applied to given types:
required: String
found: no arguments"
I would reccomend making disease a Set of Strings.
Set<String> diseases = new HashSet<String>();
void addDisease(String disease) {
diseases.add(disease);
}
void removeDisease(String deisease) {
diseases.remove(disease);
}
Sets are "better", in this case, than other Collections because they cannot hold duplicates.
You should give the class a List<String> such as an ArrayList<String> and put the diseases in this List.
Better still, create a class or enum of Disease and have your Passenger class use a List<Disease> and avoid over-use of String. You could then give the class public addDisease(Disease disease) and removeDisease(Disease disease) methods.
Incidentally, your class above should be named Passenger, the singular, not Passengers, the plural, since it represents the concept of a single Passenger.
For your requirement if you are using List like ArrayList you can access your elements(disease names) by index, but it will allow duplicate data to be inserted(same disease may be added multiple times, it will unnecessary increase in number of diseases and may arise some problems).
If you use Set like HashSet it will allow unique element only, so no issues related to duplicated entries but at the same time you can't access a particular disease by index (if you need so, as of now I am not aware of your further requirement).
So as best of my knowledge I suggest you to use LinkedHashSet(HashSet with Linked approach) it will provide you FIFO order without duplicate insertion problem.
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.