I want to know how can I save different parameters related to one Key in a single list.
For example imagine I have a list of people who have a name, a family name, a salary and a Key to find any unique person in the list.
What can I use to store these information in single list?
What are structures that I can use? Like List, Array List, Hash Map, Map, Set etc.
Do I have to define a new class?
You should probably define a Person class having all the properties you mentioned. Then you can store the people in a Map<Integer,Person> or Map<String,Person>, depending on whether the unique identifier of a Person is an Integer or a String.
If you use the HashMap implementation of Map, you'll be able to locate a Person by its identifier key in expected constant time.
You don't have to, but I would strongly recommend to define a class. It could look like this:
package com.yourcompany.projectname;
public class Person {
public String name;
public String familyName;
public BigDecimal salary;
}
then store it in a HashMap:
public static void main(String[] args) {
Map <String, Person> persons = new HasMap <>();
// define a new person
Person dady = new Person();
dady.name = "Tim";
dady.familyName = "Smith";
dady.salary = new BigDecimal("1234.56");
// store it
persons.put("dady", dady);
// retrieve it
Person misterX = persons.get("dady");
// use it
System.out.println("MisterX: "+misterX.familyName);
}
You can use a Map where a person would be a Person class with the attributes you talked about and the key would be a String that identifies each Person entity.
Then, you could just do something like:
Map<String,Person> peopleMap = new Map<String,Person>();
Person person = new Person(); //let's imagine this person has a string id attribute with a get method defined
peopleMap.put(person.getId(),person); //adds the person to the list
peopleMap.get("p71"); //assume we're looking for a person which id="p71";
The person class could be something in these lines:
public class Person {
private String name;
private String id;
private int age;
public String getId() {
return this.id;
}
//other getter/setters and methods you implement
}
Related
Lets say, Employee class has three properties.
class Employee {
int id;
String name;
String team;
public Employee(){
this.id = id;
this.name = name;
this.team = team;
}
}
I want to remove team from the object before putting into HashMap.
Map<Integer, Employee> empMap = new HashMap<>();
Employee e1 = new Employee(100, "John", "Dev");
Employee e2 = new Employee(101, "Mary", "Dev");
Employee e3 = new Employee(103, "Andy", "QA");
empMap.put(e1.getId(), e1);
empMap.put(e2.getId(), e2);
empMap.put(e1.getId(), e3);
The values in empMap shouldn't have team property in it. Creating new objects would work but it is costly in real time. Is there a way to achieve this without creating new objects.
One option, as you have mentioned, is to create new objects without the team property. Another is to use a façade
public class MapEmpFacade extends Employee {
public MapEmpFacade(Employee emp) {
//define all methods to return the method results from emp, except for getTeam
}
public int getTeam() { return null; } //override getTeam
}
You may review your design to be sure you need the property to be there.
You are holding Employee references in the map, the team property is there and probably is correctly representing an Employee ... you can just avoid using the team property ... or maybe you want to have a Person class.
If you are using those instances from some framework to do something (e.g.: serialization, persistence, etc) it should probably provide a way to ignore/skip a property in your object.
I have a list with some Person Object. Here is my Person class:
public class Person(){
// contructors
private String lastname;
private String firstname;
private List<Place> places;
// getters and setters
}
My Place class is :
public class Place(){
// contructors
private String town;
// getters and setters
}
I have code to remove some places from person:
List<Person> persons = new ArrayList<Person>();
// adding persons to List
// Below i want to remove person whose place is in town Paris.
persons.removeIf((Person person)-> person.getPlaces(???));
I want to remove a person from the list whose Place meets the following condition place.getTown()=="Paris"
How to write this code?
Add a method hasPlace to Person class:
public boolean hasPlace(String townName) {
return places.stream()
.map(Place::getTown)
.anyMatch(townName::equals);
}
Then, you can use this in the predicate given to the removeIf statement:
persons.removeIf(person -> person.hasPlace("Paris"));
Stream over the list of Places to determine if it contains a Place whose town is "Paris":
persons.removeIf(p-> p.getPlaces().stream().anyMatch(pl->pl.getTown().equals("Paris")));
If you don't want to use removeIf method, you could use Filter to
persons.stream().filter(p-> !p.getPlaces().stream().anyMatch(pl->pl.getTown().equals("Paris")));
I have two classes, Student and Book.
I want to store book name returned by student.getBook(that will return Set<Book>) into List<String>.
Student.java
public class Student {
private int id;
private String bookName;
//Setter and getter.......
}
Student.java
public class Student {
private int id;
private String student Name;
private Set<Book> books;
//Setters and Getters
}
Here is main method
public static void main(String[] arg){
Book b1= new Book(1, "art");
Book b2= new Book(2, "science");
Book b3= new Book(3, "bio");
Set<Book> b=new HashSet<>();
b.add(b1);
b.add(b2);
b.add(b3);
Student s=new Student (1, "std1",b);
//here I want to store bookName
//into List, some thing like
List<String> book=new
ArrayList<>(s.getBooks().getBookName());
}
Please help.....
This is where the Stream API comes in handy!
To turn a set of books (s.getBooks()) directly into a List<String> with book names, you just need to map and collect.
List<String> bookNames = s.getBooks().stream()
.map(x -> x.getBookName())
.collect(Collectors.toList())
This might look new to you, so I'll explain it bit by bit.
The stream method creates a Stream<Book>. This is so that you can do cool operations, like map, reduce and filter on the Set. Then we map the stream of books. map is just another word for "transform". Because you only want the book names, we transform each book in the stream into its book name. The x -> x.getBookName() describes how we want to transform it. It means "given a book x, we get the book name of x". And finally, we call collect, which "collects" everything in the stream and puts them back in a collection. In this case we want a List so we call toList().
Since s.getBookNames() returns the Set<Book>, you must iterate over the collection to fetch individual book's name and then save it to another List in your code named as book :
List<String> bookNames = new ArrayList<>(); // initialize a list for all the book names
s.getBooks().forEach(book -> bookNames.add(book.getBookName())); // iterate all the books and add their names to the above list
Also, this assumed few obvious things as pointed in comments as well,
private String student Name; // this variable either named as 'student or `name` or `studentName`
and the first model is for the Book.java instead of Student.java.
Someway, I see your modal definition for student is not good. Simply you can define Student modal as follow.
And create a separate public method in Student class to fetch the booknames.
public class Student {
private int id;
private String name;
private Set<Book> books;
public Student(int id, String name, Set<Book> b) {
this.id = id;
this.name = name;
this.books = b;
}
// all getter and setter.
public List<String> getBookNames(){
return this.getBooks().stream().map(x -> x.getName()).collect(Collectors.toList());
}
}
if you are using java1.8, as sweeper suggested you can use stream api to do your work.
public static void main(String[] args) {
Book b1 = new Book(1, "art");
Book b2 = new Book(2, "science");
Book b3 = new Book(3, "bio");
Set<Book> b = new HashSet<>();
b.add(b1);
b.add(b2);
b.add(b3);
Student s = new Student(1, "std1", b);
List<String> books = s.getBookNames();
System.out.println(books);
}
I want to make my immutable class EmployeeDetails which has Employee object in it. I have followed conditions to make class immutable:
1. class is final
2. class members are final
3. no setters
If EmployeeDetails is immutable, I should not be able to change content in it. Still, I can change employee name or id.
What I am missing here?
public class TestImmutable{
public static void main(String args[]){
EmployeeDetails empd1 = new EmployeeDetails("ABC", new Employee(1, "n1"));
System.out.println("Id : " + empd1.getEmployee().getId());
System.out.println("Name : " + empd1.getEmployee().getName());
System.out.println("Empr : " + empd1.getEmployer());
empd1.getEmployee().setId(2);
empd1.getEmployee().setName("n2");
System.out.println("\nId : " + empd1.getEmployee().getId());
System.out.println("Name : " + empd1.getEmployee().getName());
System.out.println("Empr : " + empd1.getEmployer());
}
}
final class EmployeeDetails{
private final String employer;
private final Employee emp1;
public EmployeeDetails(String employer, Employee emp1){
this.employer = employer;
this.emp1 = emp1;
}
public String getEmployer(){
return this.employer;
}
public Employee getEmployee(){
return this.emp1;
}
}
class Employee{
public int id;
public String name;
public Employee(int id, String name){
this.id = id;
this.name = name;
}
public int getId(){
return this.id;
}
public String getName(){
return this.name;
}
public void setId(int id){
this.id = id;
}
public void setName(String name){
this.name = name;
}
}
Get rid of getEmployee(). You shouldn't be able to reach Employee emp1 outside EmployeeDetails. If you need access to the fields in Employee emp1, then provide public methods that return them.
For example:
final class EmployeeDetails{
private final String employer;
private final Employee emp1;
public EmployeeDetails(String employer, Employee emp1){
this.employer = employer;
this.emp1 = emp1;
}
public String getEmployer(){
return this.employer;
}
public String getEmployeeName() {
return this.emp1.getName();
}
...
}
I have followed conditions to make class immutable: 1. class is final
2. class members are final 3. no setters
The conditions you have laid out are necessary but not sufficient to make a class immutable. Confused?
Immutability is about preserving the state of class instances for all time. Once an instance of a class is created, then all the attributes that comprise the state of that instance must remain forever unchanged.
What happens if 1 through 3 above are satisfied, but one of your instance fields is a mutable class? In this case, returning the reference to that instance field to a client makes it possible for the client to mutate the state of your supposedly immutable class.
One solution is to perform defensive copying upon all instance fields of the immutable class that are, themselves, mutable. Instead of...
public Employee getEmployee(){
return this.emp1;
}
change this code so that a new copy of the Employee object is returned to the client. This assures that clients can not get a reference to the internal state of the instances of your immutable class:
public Employee getEmployee(){
return this.emp1.clone(); // this solution assumes that Employee
// is safely cloneable, which requires some
// care on your part. An alternative is
// to define a copy constructor in the
// Employee class and: return new Employee(emp1);
}
Defensive copying is necessary for all mutable components of an immutable class, and this rule must be applied during both construction and field access. Otherwise, you make it possible for client code to retain a reference to the mutable internal state of your class.
Making a variable final means that you cannot assign it again to some other object. You can still modify the state of the object whose reference it holds.
In this case :
final class EmployeeDetails{
private final String employer;
**private final Employee emp1;**
}
You cannot assign emp1 to a new object but you can still change the state of the employee object as it is not immutable. You can make Employee objects immutable by removing all the setters.
The EmployeeDetails class is not immutable. You have followed the usual rules for immutability except one. In Joshua Bloch's Effective Java this rule is stated as follows:
Ensure exclusive access to any mutable componenent.
In your case, since the class Employee is mutable, you need to copy Employee instances in the getter and the constructor.
public Employee getEmployee() {
return new Employee(emp1.getId(), empl.getName()); // Copy
}
public EmployeeDetails(String employer, Employee emp1){
this.employer = employer;
this.emp1 = new Employee(emp1.getId(), empl.getName()); // Copy
}
Since this version uses copies, it is not possible to modify the internals of the EmployeeDetails class.
This solution is very common. For example, the String class is immutable. Both the constructor String(char[] value) and the method char[] toCharArray() make copies of the array. This is necessary because arrays are mutable.
Another solution, which may be more appropriate for your situation, is to make Employee immutable as well.
You could even get rid of the Employee class completely and just use two fields in the EmployeeDetails class.
I have two classes, Person and Employee. Employee extends Person.
I have a method that reads a Person from storage, and am writing the method that reads an Employee.
I'd like to reuse the method I have for Person to read the properties that are the same for Employee, without copy-pasting the code, but can't seem to find a way to do it.
public Person getPersonFromStorage() {
Person person = new Person();
// ... logic
return person;
}
public Employee getEmployeeFromStorage() {
Employee employee = new Employee();
// ... logic for employee-specific fields
// I want to read the fields inherited from Person here
return employee;
}
I cannot cast the retrieved Person from getPersonFromStorage, because it is not an Employee. It could be, because it's not another subtype either, but it's not.
I could do the following:
public Person getPersonFromStorage(Person person) {
if(person==null) { person = new Person(); }
// ... logic
return person;
}
public Employee getEmployeeFromStorage() {
Employee employee = (Employee) getPersonFromStorage(new Employee());
// ... logic for employee-specific fields
return employee;
}
But I'd like to avoid this complexity if I can. I have the feeling I'm overlooking something elementary. Is there a better way to solve this?
Just offering a different architecture that I usually use in this instance. If you're talking about "from storage", to me that means some sort of persistent structure. Text File, database, etc. For the following example, let's assume you have your values in a text file.
Assume a file employees.txt, which contains one employee:
// Dave's person details.
Dave
Manchester
32
// Dave's employee details
Assassin
Mostly North Korea.
Then you've got a class Person, that looks a little like this:
public class Person
{
private String name;
private String location;
private int age;
public Person(String name, String location, int age)
{
// blah blah blah.
}
}
And a class Employee that looks like this:
public class Employee extends Person
{
private String jobTitle;
private String area;
public Employee() {
// etc.
}
}
In your Person class, you can create a constructor designed to read the parameters for a Person. Something like:
public Person(Scanner file)
{
this.name = file.nextLine();
this.location = file.nextLine();
this.age = file.nextInt();
file.nextLine(); // Make sure you're pointing at the new line!
}
And in your Employee class, you can create a constructor designed to read the parameters for an employee, while calling it's superclass to deal with the other values.
public Employee(Scanner file)
{
super(file);
this.jobTitle = scanner.nextLine();
this.area = scanner.nextLine();
}
Then all you have to do to call this is something like:
Scanner s = new Scanner("employees.txt");
Person p = new Employee(s);
or to make it more compact:
Person p = new Employee(new Scanner("employees.txt"));
This will go and parse the file, and return an object, while wrapping up all the logic for actually reading the file inside the classes that need the data.
Not a text file?
Well, it's not really vital. The important thing is just passing an object up the call chain, and those methods are doing what that particular class needs to do, then passing on the object. If it's a database row, it's exactly the same principle.
Your second code example is the way to go, except you don't even need the null check line. Just pass in a non-null Person that you've instantiated elsewhere.
For even better abstraction, see if you can make Person into an abstract class.
More elegant way is to overload Employee constructor to be able to create Employee instance from parent Person instance.
public Employee getEmployeeFromStorage() {
Employee employee = new Employee(getPersonFromStorage());
// ... logic for employee-specific fields
return employee;
}
You could use a protected factory method.
protected Person createNewInstance() { return new Person(); }
and use this in your getPersonFromStorage() method. The subclass would then override this method, thus changing the return type into an Employee, which you can then use like in your second example.
public class Person {
public Person getPersonFromStorage() {
Person person = createNewInstance();
// ... logic
return person;
}
protected Person createNewInstance() {
return new Person();
}
}
public class Employee extends Person {
public Employee getEmployeeFromStorage() {
Employee employee = (Employee) getPersonFromStorage();
// ... logic for employee-specific fields
return employee;
}
protected Person createNewInstance() {
return new Employee();
}
}
Alternatively you could also create an Employee constructor based on a Person
public class Employee extends Person {
public Employee(Person person) {
super();
// copy all fields from person
}
public static Employee getEmployeeFromStorage() {
Employee employee = new Employee(getPersonFromStorage());
// ... logic for employee-specific fields
return employee;
}
}
I also added static to the methods, assuming you're intending to create new instances with no direct relationship to existing objects. This would not work with the first variant.