Immutable Java Class with List as field - java

Can we make the class immutable which has collection as one of the fields?
public class Student implements Comparable<Student> {
private int rollNumber;
private String name;
private Set<String> subjects;
private List<Integer> marks ;
public Student(int rollNumber, String name, Set<String> subjects,
List<Integer> marks) {
this.rollNumber = rollNumber;
this.name = name;
this.subjects = Collections.unmodifiableSet(subjects);
this.marks = Collections.unmodifiableList(marks);
setPercentage();
}
private float percentage;
public int getRollNumber() {
return rollNumber;
}
public String getName() {
return name;
}
public Set<String> getSubjects() {
return new HashSet<>(subjects);
}
public List<Integer> getMarks() {
return new ArrayList<>(marks);
}
public float getPercentage() {
return percentage;
}
private void setPercentage() {
float sum = 0;
for (Integer i : marks)
sum = sum + i;
if (!marks.isEmpty())
percentage = sum / marks.size();
}
}
I am not able to achieve it.
I tried:
Set<String> subjects= new HashSet<>();
subjects.add("Maths");
subjects.add("Science");
subjects.add("English");
List<Integer> marks1= new LinkedList<Integer>();
marks1.add(45);
marks1.add(36);
marks1.add(98);
Student student1= new Student(1, "Payal", subjects, marks1);
//student1.getSubjects().add("History");
subjects.add("History");
System.out.println(student1);
But subjects.add is changing the state of the object.
Please help.

You're making a copy of both collections before returning them from your getters. This is unnecessary, since the collections are unmodifiable (unless you want the caller to get mutable collections and not unmodifiable ones).
What is necessary is to make copies of the collections that are passed from the outside in the contructor. Otherwise, the caller can still modify the collections after they've been stored in your object:
this.subjects = Collections.unmodifiableSet(new HashSet<>(subjects));
this.marks = Collections.unmodifiableList(new ArrayList<>(marks));
To be truly immutable, the class and its fields should also be final.

public class Student implements Comparable<Student> {
private int rollNumber;
private String name;
private Set<String> subjects;
private List<Integer> marks ;
public Student(int rollNumber, String name, Set<String> subjects,
List<Integer> marks) {
this.rollNumber = rollNumber;
this.name = name;
this.subjects = new HashSet<>(subjects);
this.marks = new ArrayList<>(marks);
setPercentage();
}
private float percentage;
public int getRollNumber() {
return rollNumber;
}
public String getName() {
return name;
}
public Set<String> getSubjects() {
return new HashSet<>(subjects);
}
public List<Integer> getMarks() {
return new ArrayList<>(marks);
}
public float getPercentage() {
return percentage;
}
private void setPercentage() {
float sum = 0;
for (Integer i : marks)
sum = sum + i;
if (!marks.isEmpty())
percentage = sum / marks.size();
}
}
I did this. It worked. If I try these 2 operations now, no problem.
Set<String> subjects= new HashSet<>();
subjects.add("Maths");
subjects.add("Science");
subjects.add("English");
List<Integer> marks1= new LinkedList<Integer>();
marks1.add(45);
marks1.add(36);
marks1.add(98);
Student student1= new Student(1, "Payal", subjects, marks1);
student1.getSubjects().add("History");
subjects.add("History");
System.out.println(student1);

Here is the answer
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Student implements Comparable<Student> {
private final int rollNumber;
private final String name;
private final Set<String> subjects;
private final List<Integer> marks;
public Student(int rollNumber, String name, Set<String> subjects, List<Integer> marks) {
this.rollNumber = rollNumber;
this.name = name;
this.subjects = new HashSet<>(subjects);
this.marks = new ArrayList<>(marks);
setPercentage();
}
private float percentage;
public int getRollNumber() {
return rollNumber;
}
public String getName() {
return name;
}
public Set<String> getSubjects() {
return Collections.unmodifiableSet(subjects);
}
public List<Integer> getMarks() {
return new ArrayList<>(marks);
}
public float getPercentage() {
return percentage;
}
private void setPercentage() {
float sum = 0;
for (Integer i : marks) {
sum = sum + i;
}
if (!marks.isEmpty()) {
percentage = sum / marks.size();
}
}
#Override
public String toString() {
return subjects.toString();
}
#Override
public int compareTo(Student o) {
return -1;
}
}
Main Method :
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
public class NewClass {
public static void main(String[] args) {
Set<String> sub = new HashSet<>();
sub.add("Maths");
sub.add("Science");
sub.add("English");
List<Integer> marks1 = new LinkedList<Integer>();
marks1.add(45);
marks1.add(36);
marks1.add(98);
Student student1 = new Student(1, "Payal", sub, marks1);
sub.add("History");
System.out.println(student1);
}
}
1 : Reason why other code is not working is they make collection unmodifiableSet and unmodifiableList but to the local object while we just need to create new object instead of pointing old reference.
2 : And Second prevent modification of return value, for that just make instance variable to final or you can create new collection object and return it, but if you do that then it create new object each time you call getXXXX method while actually you don't need that object.

Related

Send array data from one class to another JAVA

(I'm a beginner so this may sound obvious/lack information.) I have an ArrayList of attributes for different pets including attributes such as their given-name, common-name, the price of the animal, sex, date bought and date sold. this information is generated from a separate class that adds an array of information to an array of arrays of the already existing list of animals. Essentially, I want to send the array to another class (called Pets) so it can then be added to the array of arrays. I understand this may sound confusing but this is the only way I can word it, I can clarify anything if needed. Any help would be great as I'm really stuck and can't work out how to send it. This is the code that generates my values in the array (using text-boxes to input the information).
public void actionPerformed(ActionEvent e) {
ArrayList<String> NewanimalArr = new ArrayList<>();
String givenName = txtGivenname.getText();
String commonName = txtCommonName.getText();
String priceOf = txtPrice_1.getText();
String sexOf = txtSex.getText();
String colourOf = txtMaincolour.getText();
String dateOfA = txtArrivaldate.getText();
String dateSold = txtSellingdate.getText();
NewanimalArr.add(givenName);
NewanimalArr.add(commonName);
NewanimalArr.add(priceOf);
NewanimalArr.add(sexOf);
NewanimalArr.add(colourOf);
NewanimalArr.add(dateOfA);
NewanimalArr.add(dateSold);
System.out.println(NewanimalArr);
}
});
this will then print information generated that is entered for example:
[alex, Dog, 40.50, Male, Brown, 14/04/2015, 14/12/2016]
how do I then send this data to another class
Option one Constructor Injection:
public class Foo {
List<String> actionPerformed(ActionEvent e) {
List<String> newanimalArr = new ArrayList<>();
.....
return newanimalArr
}
...
public class Pets {
private final List<String> array;
public Pets(final List<String> array) {
this.array = array;
}
void bar() {
System.out.println(this.array);
}
}
....
public static void main(String[] args) {
Foo foo = new Foo();
Pets pets = new Pets(foo.actionPerformed( new ActionEvent() ) );
pets.bar();
}
Option two Getter-Setter Injection:
public class Foo {
private final List<String> newanimalArr;
public Foo() {
this.newanimalArr = new ArrayList<>();
}
public void actionPerformed(ActionEvent e) {
.....
}
public List<String> getNewanimalArr() {
return new ArrayList<String>(newanimalArr);
}
}
...
public class Pets {
private List<String> array;
public Pets() {
this.array = Collections.<String>emptyList();
}
public void setArray(final List<String> array) {
this.array = array;
}
public void bar() {
System.out.println(this.array);
}
}
....
public static void main(String[] args) {
Foo foo = new Foo();
foo.actionPerformed( new ActionEvent() );
Pets pets = new Pets();
bar.setArray( foo.getNewanimalArr() );
pets.bar();
}
See also Dependency Injection Patterns
Create a class definition of Pet, using instance variables for the fields. In Java it is custom to create a setXyz and a getXyz for each xyz field. You can also create a constructor in which you pass all the values and assign them to the fields, this minimizes the risk of fields not being filled in.
The initial ArrayList you are creating doesn't add that much use, it is easier to create the Pet instances directly:
List<Pet> newArrivals = new ArrayList<>();
// get data from view fields and if necessary transform them to other objects such as:
LocalDate arrivedOn = LocalDate.parse(txtArrivaldate.getText(), DateTimeFormatter.ofLocalizedDate(FormatStyle.FormatStyle);
// create and add a new Pet object to the list
newArrivals.add(new Pet(.....));
public class Pet {
public enum Gender {
FEMALE, MALE
}
private String givenName;
private String commonName;
private double price;
private Gender gender;
private String color;
private LocalDate arrivedOn;
private LocalDate soldOn;
public Pet() {
}
public Pet(String givenName, String commonName, double price, Gender gender, String color, LocalDate arrivedOn,
LocalDate soldOn) {
super();
this.givenName = givenName;
this.commonName = commonName;
this.price = price;
this.gender = gender;
this.color = color;
this.arrivedOn = arrivedOn;
this.soldOn = soldOn;
}
public String getGivenName() {
return givenName;
}
public void setGivenName(String givenName) {
this.givenName = givenName;
}
public String getCommonName() {
return commonName;
}
public void setCommonName(String commonName) {
this.commonName = commonName;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public LocalDate getArrivedOn() {
return arrivedOn;
}
public void setArrivedOn(LocalDate arrivedOn) {
this.arrivedOn = arrivedOn;
}
public LocalDate getSoldOn() {
return soldOn;
}
public void setSoldOn(LocalDate soldOn) {
this.soldOn = soldOn;
}
}

Java: Storing an Array into an Array?

Is it possible to store three string values added into an array (studentName), and store that into a different array so it can be found later?
Basically my main goal is to store a name, user id, and a balance (fullName, idName, 300).
And add that into a "super(?)" array so when people type down, it finds the fullName and pulls the information from there.
You can create a class
public class Student {
private String name;
private String id;
private int balance;
}
and then you can create a list of these objects:
List<Student> list = new ArrayList<Student>();
Map<String, String> map = new HashMap<String, String>();
then:
List<Map<String, String>> listOfMaps = new ArrayList<Map<String, String>>();
and then:
map.put("name", "Thomas");
map.put("id", "Thomas id");
map.put("balance", ""300);
listOfMaps.add(map);
Anyhow, be careful. You will have to keep numbers (f.e. balance) as a String and after you will need to map it.
Well, I believe you are talking about something like Jagged Array which is available in C# but for java, we can do it in some other ways... like creating a class and manipulating it as Generic List implementation...
public class Student {
private String name;
private int id;
private int balanace;
public Student(){}
public Student(String name, int id, int balance){
this.name = name;
this.id = id;
this.balanace = balance;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getBalanace() {
return balanace;
}
public void setBalanace(int balanace) {
this.balanace = balanace;
}
}
In some other class where you would want to manipulate
public class ManipulateData {
public static void main(String[] args){
Student student1 = new Student("James", 1, 500);
List<Student> list = new ArrayList<Student>();
list.add(student1);
for(Student s: list){
System.out.println("Name : " + s.getName());
System.out.println("ID : " + s.getId());
System.out.println("Balance : " + s.getBalanace());
}
}
}

Sort JList by name

What I am doing is getting elements from a map and adding them onto a JList to display on a GUI. I want to know how to sort the names alphabetically.
private void refreshShopsList() {
gameShopsJList.setModel(new javax.swing.AbstractListModel<String>() {
public int getSize() {
return ShopsLoader.getShops().size();
}
public String getElementAt(int i) {
return getShopByIndex(i).getName();
}
});
}
private Shop getShopByIndex(int index) {
Iterator<Entry<String, Shop>> it = ShopsLoader.getShops().entrySet().iterator();
int count = -1;
while(it.hasNext()) {
Entry<String, Shop> entry = it.next();
count++;
if (count == index)
return entry.getValue();
}
return null;
}
/**
* The map of the shops
*/
private static final Map<String, Shop> shops = new HashMap<String, Shop>();
public static Map<String, Shop> getShops() {
return shops;
}
Here is a little example, which sorts your shop names.
The ShopComparator class does the sorting task:
package model;
import java.util.Comparator;
public class ShopComparator implements Comparator<Shop> {
#Override
public int compare(Shop o1, Shop o2) {
return o1.getName().compareTo(o2.getName());
}
}
The Shop class, as simple as possible:
package model;
public class Shop {
private int id;
private String name;
public Shop(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And the main app:
package model;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
public class App {
public static void main(String[] args) {
Map<String, Shop> shops = new HashMap<String, Shop>();
Shop s1 = new Shop(1, "Apus Drugstore");
Shop s2 = new Shop(2, "DM");
Shop s3 = new Shop(3, "Kaufhof");
Shop s4 = new Shop(4, "Moes Traverne");
shops.put("one", s3);
shops.put("two", s4);
shops.put("three", s1);
shops.put("four", s2);
for(Shop s : shops.values()) {
System.out.println(s.getName());
}
ShopComparator sc = new ShopComparator();
TreeSet<Shop> sortedShops = new TreeSet<>(sc);
sortedShops.addAll(shops.values());
for(Shop s : sortedShops) {
System.out.println(s.getName());
}
}
}
First output, unsorted:
Moes Traverne
Kaufhof
Apus Drugstore
DM
and the sorted output.
Apus Drugstore
DM
Kaufhof
Moes Traverne
Algorithm:
get all values from JList, convert them to strings, store in array
sort the array
set new values to JList.
code:
JList jl = new JList(new Object[]{4.5,1,"Hi!"});
ListModel model = jl.getModel();
String[] strings = new String[model.getSize()];
for(int i=0;i<strings.length;i++){
strings[i]=model.getElementAt(i).toString();
}
Arrays.sort(strings);
jl.setListData(strings);
see about Comparator if you need to sort array in any other order.

I am looking to set and an array in java while setting a sub method in the array

I have seen other array codes but here is what I got. I am trying to set the courseID in the CollegeCollege course in the array. I need to set the array in the student.java
public class CollegeCourse {
private static String courseID;
private static int creditHours;
private static char grade;
public static void setCourseID(String course){
courseID = course;
}
public static String getCourseID(){
return courseID;
}
public static void setCreditHours(int CH){
creditHours = CH;
}
public static int getCreditHours(){
return creditHours;
}
public static void setGrade(char g){
grade = g;
}
public static char getGrade(){
return grade;
}
public class Student {
private static int IDnumber;
private static CollegeCourse[] course = new CollegeCourse[5];
public static void setIDnumber(int x){
IDnumber = x;
}
public static int getIDnumber(){
return IDnumber;
}
public static String getCourse(int x){
return course[x].getCourseID();
}
public static void setCourse(CollegeCourse newCourse, int ID){
CollegeCourse.setCourseID = newCourse[ID];
}
}
It could seem like you are trying to code that the student takes course with id 5. Maybe your classes should have constructors taking arguments based on what ID the course has.
and I don't know what you need the fields to be static for, bt feel free to have them static if it is needed for some reason.
public class CollegeCourse {
private String courseID;
private int creditHours;
private char grade;
public CollegeCourse(String courseID) {
this.courseID = courseID;
}
...
public class Student {
...
private static CollegeCourse[] course = new CollegeCourse("5");
...
I think a better design would be this:
public class CollegeCourse {
private final String courseId;
private final int creditHours;
public CollegeCourse(final String courseId, int creditHours) {
this.courseId = courseId;
this.creditHours = creditHours;
}
public String getCourseId() {
return courseId;
}
public int getCreditHours() {
return creditHours;
}
#Override
public boolean equals(Object o) {
if (o == this) { return true; }
if (!(o instanceof CollegeCourse)) { return false; }
CollegeCourse cc = (CollegeCourse)o;
return courseId.equals(cc.courseId)
&& creditHours == cc.creditHours;
}
#Override
public int hashCode() {
int result = 17;
result = 31 * result + courseId.hashCode();
result = 31 * result + creditHours;
return result;
}
};
public enum Grade { A, B, C, D, E };
public class Student {
private final String id;
private final Map<CollegeCourse, Grade> course2GradeMap = new HashMap<>();
public Student(final String id) {
this.id = id;
}
public Grade getGrade(final CollegeCourse course) {
return couse2GradeMap.get(course);
}
public void addCollegeCourse(final CollegeCourse course, Grade grade) {
course2GradeMap.put(course, grade);
}
public Collection<CollegeCourse> getCollegeCourses() {
return Collections.unmodifiableCollection(course2GradeMap.values());
}
};
Use it in this way:
Student student = new Student("001");
student.addCollegeCourse(new CollegeCourse('alg', 100), Grade.A);
student.addCollegeCourse(new CollegeCourse('stat', 100), Grade.C);

showing null pointer exception with junit test case

i am trying to sort the employee object based on salary using the junit test case.
it is my employee sort class
package day4;
import day4.Employee;
public class EmployeesInfoWithSalary {
private Employee[] employee;
private int numberOfEmployees;
public EmployeesInfoWithSalary(Employee[] employee, int numberOfEmployees) {
super();
this.employee = employee;
this.numberOfEmployees = numberOfEmployees;
}
public Employee[] getSortBasedOnSalary() {
String temp;
for (int iterator = 0; iterator < numberOfEmployees; iterator++) {
int minSalary = employee[iterator].getSalary();
int index = iterator;
for (int comparator = iterator; comparator < numberOfEmployees; comparator++) {
if (employee[comparator].getSalary() < minSalary) {
index = comparator;
minSalary = employee[comparator].getSalary();
}
}
employee[index].setSalary(employee[iterator].getSalary());
employee[iterator].setSalary(minSalary);
temp = employee[index].getId();
employee[index].setId(employee[iterator].getId());
employee[iterator].setId(temp);
temp = employee[index].getName();
employee[index].setName(employee[iterator].getName());
employee[iterator].setName(temp);
}
return employee;
}
}
employee object class is as follows
package day4;
public class Employee {
private String id;
private String name;
private int salary;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
testemployee salary class for junit test case is as follows
package day4;
import static org.junit.Assert.*;
import day4.Employee;
import org.junit.Test;
public class TestEmployeeInfoWithSalary {
#Test
public void testGetSortBasedOnSalary() {
Employee[] employee = new Employee[5];
employee[0].setName("pratap");
employee[1].setName("aruna");
employee[2].setName("satyam");
employee[3].setName("krishna");
employee[4].setName("siva");
employee[0].setId("k0100");
employee[1].setId("k0101");
employee[2].setId("k0102");
employee[3].setId("k0103");
employee[4].setId("k0104");
employee[0].setSalary(10000);
employee[1].setSalary(1000);
employee[2].setSalary(8000);
employee[3].setSalary(6000);
employee[4].setSalary(9000);
EmployeesInfoWithSalary employeeInfoWithSalary= new EmployeesInfoWithSalary(employee, 5);
employee[4].setName("pratap");
employee[0].setName("aruna");
employee[2].setName("satyam");
employee[1].setName("krishna");
employee[3].setName("siva");
employee[4].setId("k0100");
employee[0].setId("k0101");
employee[2].setId("k0102");
employee[1].setId("k0103");
employee[3].setId("k0104");
employee[4].setSalary(10000);
employee[0].setSalary(1000);
employee[2].setSalary(8000);
employee[1].setSalary(6000);
employee[3].setSalary(9000);
assertArrayEquals(employee,employeeInfoWithSalary.getSortBasedOnSalary());
}
}
the log is showing the error that null point expression..
can any body help me..
thanks..
I suspect this is the line of the NPE.
// creates an array full of null values.
Employee[] employee = new Employee[5];
employee[0].setName("pratap");
You need to add Employee objects to each element in the array.
A better approach is to use a constructor which takes all the needed fields.
Employee[] employee = {
new Employee("pratap", "k0100", 10000),
new Employee("aruna", "k0101", 1000),
new Employee("satyam", "k0102", 8000),
new Employee("krishna","k0103", 6000),
new Employee("siva", "k0104", 9000) };
After
Employee[] employee = new Employee[5];
for each index in array you need to initialize Employee object.
employee[0] = new Employee(); etc

Categories

Resources