Is there a better way of accessing ArrayList object elements? - java

Took me a bit to figure this out but Im just wondering if there is a cleaner way to do this
this is the gist of my main
public class Main {
private static Bank Chase = new Bank();
//This is the function in main to add a transaction to a specified customer of a branch
public static void addTransaction() {
System.out.println("Enter the name of the branch");
String branch = scanner.nextLine();
System.out.println("Enter the name of the person");
String name = scanner.nextLine();
System.out.println("Enter the amount you would like to add");
double amount = scanner.nextDouble();
scanner.nextLine();
Chase.getBranchList().get(Chase.branchIndex(branch)).getCustomerList().get(Chase.getBranchList().get(Chase.branchIndex(branch)).customerIndex(name)).addTransaction(amount);
}
}
This last line is really long and confusing to others this is what it does
//gets the branchlist -> gets the specified branch -> gets the customerlist -> finds the specified customer -> adds transaction
these are the other relevant parts of the classes the function references
public class Bank {
private ArrayList<Branch> branchList = new ArrayList<Branch>();
public ArrayList<Branch> getBranchList() {
return branchList;
}
public int branchIndex(String name){
for(Branch branch: branchList){
if(branch.getName().equals(name)){
return branchList.indexOf(branch);
}
}
return -1;
}
}
public class Branch {
private String branchName;
private ArrayList<Customer> customerList;
public ArrayList<Customer> getCustomerList() {
return customerList;
}
public int customerIndex(String name){
for(Customer customer: customerList){
if(customer.getName().equals(name)){
return customerList.indexOf(customer);
}
}
return -1;
}
public class Customer {
private String customerName;
private ArrayList<Double> transactions = new ArrayList<Double>();
public Customer(String customerName, double amount) {
this.customerName = customerName;
this.transactions = new ArrayList<Double>();
transactions.add(amount);
}
public String getName() {
return customerName;
}
public void addTransaction(double transaction){
transactions.add(transaction);
}
}
So is there any more readable way of accessing these elements that are in object ArrayLists? I think the last line in addTransaction() looks a bit redundant.

Rather than one long line you could
a) split the code into multiple lines
Chase.getBranchList().get(Chase.branchIndex(branch))
.getCustomerList()
.get(Chase.getBranchList()
.get(Chase.branchIndex(branch))
.customerIndex(name))
.addTransaction(amount);
b) stored the returned values of each get into a local variable, especially the code that it re-calling the same methods e.g. Chase.branchIndex(branch) and Chase.getBranchList()

At the moment you are assuming unique customer/branch names, and then cycling through your array list to find the customer by name. This assumption is fine, if it's a valid assumption but could mean that there are more optimal solutions. I would recommend a refactor of your code to utilise a java hash map:
https://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
Basically, this will mean that you can access the customer/bank directly by name and will simplify your code greatly! It will also have performance benefits.
For your scenario this refactor would look similar to this:
public class Branch
{
private HashMap<String, Customer> _customers;
private String _branchName;
public Branch(String branchName)
{
_branchName = branchName;
_customers = new HashMap<String, Customer>();
}
public Customer getCustomer(String customerName)
{
return _customers.get(customerName);
}
}
If you follow the same for Bank, you should be able to access a Customer and add a transaction as follows:
Chase.getBranch(branch).getCustomer(name).addTransaction(transaction);
Let me know if you need help converting Bank :)

You are on the right track, but you've got some minor design flaws.
Step 1: Add a method called getBranchByName(String branchName) to your Bank class that returns a Branch object and get rid of your branchIndex() method:
public Branch getBranchByName(String branchName) {
return branchList.stream()
.filter(branch -> branch.getBranchName().equals(branchName))
.findAny()
.get();
}
Step 2: Add a method called getCustomerByName(String name) to your Customer class that returns a Customer object and get rid of your customerIndex() method:
public Customer getCustomerByName(String name) {
return customerList.stream()
.filter(customer -> customer.getCustomerName().equals(name))
.findAny()
.get();
}
Step 3: Now, method call in your main() method becomes more compact, simple and easy to read:
Chase.getBranchByName(branchName).getCustomerByName(customerName).addTransaction(amount);
Note: I've used Java 8 streams as you can observe. If you are not allowed to use Java 8 streams, you can just stick with classic imperative style of programming by writing for() loops as you have done earlier. As a quick example, if you want to write getBranchByName(String branchName) in old fashioned Java 7 style, your loop looks like this:
for(Branch branch : branchList) {
if(branch.getBranchName().equals(branchName)){
return branch;
}
}

Related

How to write a test for a method which is filtering elements from an array based on string criteria

I have a Car class with a category field which is String:
public class Car {
private String category;
public Car(String category) {
this.category = category;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}
And I have this method which is filtering out elements from array based on String using the contains() method to keep only sport cars. I know this is bad idea, just trying to write my very first unit test. Can you please help me to understand where to start. In general what are the best practices for testing loop filtering logic.
private List<Car> filterOutEverythingExceptSportCars(List<Car> cars) {
List<Car> filteredCarsList = new ArrayList<>();
for (int i = 0; i < cars.size(); i++) {
if (cars.get(i).getCategory().contains("sport")) {
filteredCarsList.add(cars.get(i));
}
}
return filteredCarsList;
}
First of all you need to make your method testable. In your implementation it's a private instance method. Since you're not using anything else from the instance, you could make it a package-private static method:
static List<Car> filterOutEverythingExceptSportCars(List<Car> cars) {
List<Car> filteredCarsList = new ArrayList<>();
for (Car car: cars) {
if (!car.getCategory().contains("sport")) {
filteredCarsList.add(car);
}
}
return filteredCarsList;
}
Now you can easily write a unit test with input and output lists to be compared:
#Test
public static void testFilterOutEverythingExceptSportCars() {
List<Car> cars = ...;
List<Car> actual = filterOutEverythingExceptSportCars(cars);
List<Car> expected = ...;
assertEquals(expected, actual);
}
You only need to statically import the filterOutEverythingExceptSportCars method and have the test class in the same package as the implementation.
Please note that the improvement of the for-loop is up to you to apply and not mandatory. I Also didn't change the naming or if-condition to match. Just write your tests and iterate over your implementation until you like it best...
You can look at Stream and use filter operator like this.
private static List<Car> filterOutSportCars(List<Car> cars) {
return cars.stream().filter(car -> !car.category.contains("sport")).collect(Collectors.toList());
}
Just test the contract - if you put in <something>, then what do you expect back? For example, what happens if I call filterOutEverythingExceptSportsCars(null), or filterOutEverythingExceptSportsCars(Collections.emptyList()), or filterOutEverythingExceptSportsCars(Arrays.asList(car1, null, car2)), etc.
Then construct your tests around this series of preconditions, actions and expected results. So your first test may be:
#Test
givenListOfCarsIsNull_whenFilteringOutEverythingExceptSportsCars_thenReturnEmptyList() {
final List<Car> in = null; //precondition
final List<Car> out = filterOutEverythingExceptSportsCars(in); //calling the method
assertTrue(out.isEmpty()); //verifying expected output
}
If you expect back an empty list in this scenario. You may instead expect an Exception to be thrown, or something else.
May be you want to give kotlin (which compiles to java byte-code) a try:
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
class Car(var category: String)
fun sportCars(list: List<Car>) = list.filter { it.category.contains("sport") }
class CarTests {
#Test
fun `one ferrari and one truck`() {
val ferrari = Car("ferrarisportcar")
val truck = Car("truck")
val sportCars = sportCars(listOf(ferrari, truck))
assertThat(sportCars).containsOnly(ferrari)
}
}
You can mix kotlin and java. So an option might be to use java for production code and kotlin for test code.

Using a method from one class into a set

I'm having problems getting a method from one class to work if I put the objects into a set.
So I have
public class Employee {
/* instance variables */
private String firstName;
private String employeeNumber;
public Employee(String employNum) {
super();
this.employeeNumber = employNum;
}
...
public String getFirstName() {
return this.firstName;
}
There is lots of other code which I can post if needed but I'm not allowed to change the Employee class.
So for my code I have to create a class for a Set of Employees which I've done with
public class Records {
public Set<Employee> employeeSet = new HashSet<Employee>();
public Records() {
}
}
Now I need a method that will print the details of all employees in the set. Here is my attempt so far
public void printEmployeeNames() {
for (String employee : employeeSet) {
System.out.println(this.employeeSet.getFirstName());
}
}
The problem I'm having is that it won't compile as it says
"incompatible types"
and highlights employeeSet in
for (String employee : employeeSet)
My other problem is that it can't access the method for getFirstName(). I've tried to isolate the method using
public void printEmployeeNames() {
System.out.println(this.employeeSet.getFirstName());
}
This also won't compile as it states
"cannot find symbol - method getFirstName()".
Edit.
Thanks for the help with this problem, I changed it to this and it worked.
public void printEmployees()
{
for (Employee employee: employeeSet)
{
System.out.println(employee.getFirstName());
}
}
this here makes no sense:
for (String employee: employeeSet)
{
System.out.println(this.employeeSet.getFirstName());
}
since the employeeSet is a Set and sets dont have a method called getFirstName
you have to do:
for (Employee employee: employeeSet) //for every EMPLOYEE in the employeeSet
{
System.out.println(employee.getFirstName()); //from that employ get the name
}
AND create in the Employee class the respective Setter and getters
in this case:
private String firstName;
/**
* #return the employeeNumber
*/
public final String getEmployeeNumber() {
return firstName;
}
That should be
for (Employee employee: employeeSet)
{
System.out.println(employee.getFirstName());
}
Set doesn't have a firstname method. Your employee object have have.
First of all, have you heard of encapsulation? The declaration public Set<Employee> employeeSet is an example of a bad practice, and you should use a private field with some sort of getter. The reason your for loop is raising errors is that you made two mistakes:
employeeSet is a List<Employee>, whereas you are asking for a String when iterating over it. This is incorrect - change the type of employee to Employee.
You are trying to access getFirstName() from your field employeeSet. This won't work, as Set has no such method. I believe you meant to call the method on employee.
Also, you may simplify your code to the following one-liner with Java 8 streams:
public void printEmployeeNames() {
employeeSet.stream().map(Employee::getFirstName).forEach(System.out::println);
}

Java: Queues within A class

I have created a class Book that has a queue of another class I created, Employees as seen below...
class Employee{
String name;
int waiting_time;
int retaining_time;
int priority;
public Employee()
{
this.waiting_time=0;
this.retaining_time=0;
}
//getters and setters ommitted to save space
public void setPriority()
{
priority = waiting_time - retaining_time;
}
public int getPriority()
{
return priority;
}
}
class Book{
String name;
LocalDate start_date;
LocalDate end_date;
boolean archived;
Queue<Employee> Employees ;
public Book()
{
}
//getters and setters for end_date, start_date, archived ommitted to save space
public void setQueue(Queue<Employee> qa)
{
Employees = qa;
}
public Queue<Employee> getQueue()
{
return Employees;
}
When I attempt to add an Employee to the Book's queue...
public static void addEmployee(String aName, ArrayList<Book> booksToCirculate, ArrayList<Employee> employeeArray)
{
Employee anEmployee = new Employee();
anEmployee.setName(aName);
employeeArray.add(anEmployee);
for (Book b : booksToCirculate)
{
b.getQueue().add(anEmployee); //adds employee to each queue, where the error is at
}
}
I receive a NullPointerException error when trying to add an Employee to the queue and I can't seem to figure out why, I've gone through my book, and it appears as if I've done it according to an example of dogs and dogtoy's they had. Any suggestions on where I'm going wrong are much appreciated!
Also, if you have any questions on my code please ask, I'm rather new at classes and objects, but will do my best to explain myself!
It looks you need to create your queue. As you have not, it defaults to null. Hence:
b.getQueue()
is returning null.
So when you call
b.getQueue().add(...)
you are trying to call a method on null which causes the exception.
If this is the case, then the solution would be to create the queue in the Book constructor:
public Book()
{
Employees = new Deque<Employee>(); // pick an implementation of the Queue interface
}
You did not initialize the Queue. You have to initialize it before accessing it, otherwise it's initialized with null by the compiler.
Sonce Queue is an Interface you can't do
Queue<Employee> Employees = new Queue<>(); //won't work, because Queue is an interface
What you could do is use a LinkedList which implemens Queue:
Queue<Employee> Employees = new LinkedList<>();

Only Allow Objects with Unique Name - Java

I am making an inventory system.
I want to ensure that objects I am creating (Ingredients) all have unique names. In other words, I want to make sure that there are never two Ingredients that have the same name in the whole program. Currently I have the following class:
package ingredient;
import java.util.HashSet;
public class Ingredient {
private final String name;
private final double price;
private static HashSet<String> names = new HashSet<String> ();
private Ingredient(String ingr_name, double ingr_price) {
name = ingr_name;
price = ingr_price;
}
public static Ingredient createIngredient(String ingr_name, double ingr_price) {
if (names.contains(ingr_name)) {
return null;
} else {
names.add(ingr_name);
return new Ingredient(ingr_name, ingr_price);
}
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
Then, when I go to actually make new ingredients, I make statements such as :
Ingredient egg = Ingredient.createIngredient("egg", 1);
Is this okay design? I suppose I am concerned because returning "NULL" might not be the best practice here.
I cant comment, but whatever...
I would go about this by storing all of the Ingredients in a different class, then you wouldn't need all this static nonsense. In the class where you actually create a new Ingredient (Ingredient egg = Ingredient.createIngredient("egg", 1);) you could maybe create an ArrayList of ingredients like so:
ArrayList<Ingredient> ingredients = new ArrayList<>();
Then when you make a new Ingredient you would just have to make sure you add it to the ArrayListand when you do so, check that none of the ingredients are already there, maybe something like this:
createIngredient("egg", 1);
or
Ingredient egg = createIngredient("egg", 1);
...
private Ingredient createIngredient(String ingr_name, double ingr_price){
for(Ingredient i : ingredients){
if(i.getName().equals(ingr_name)){
return null;
}
}
Ingredient newing = new Ingredient(ingr_name, ingr_price);
ingredients.add(newing);
return newing;
}
Then the Ingredient class could be cut down to something like this:
package ingredient;
public class Ingredient {
private final String name;
private final double price;
public Ingredient(String ingr_name, double ingr_price) {
name = ingr_name;
price = ingr_price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
And then you could access each individual Ingredientwith a method to run through the ArrayList and find the Ingredient with the name your looking for:
public Ingredient findIngredient(String name){
for(Ingredient i : ingredients){
if(i.getName().equals(name)){
return i;
}
}
return null;
}
I would recommend either
A) returning the already created ingredient
Or if that would confuse the caller,
B) throwing an exception
This can be a simple IllegalArgumentsException, or depending on your needs, a custom exception class.

Java Concert Hall OOP design

I want to make a Booking class to book concert tickets. A ConcertHall class has 2 different seats, namely VIP and Regular. I've chosen Seat[][] as my data structure, where a Seat class contains seat_number:String and preference:String. Now, the VIP and Regular seats have a different capacity. Let's say the VIP's capacity is 10x5 seats and the Regular's capacity is 50x100 seats. The seats corresponding to both VIP and Regular also have Left, Center, and Right preferences.
The problem with my current design is I have so many redundant code . Let's say a user wants to book a concert ticket, he/she will call the method: book("VIP", "Mary", "Center"). This is what my design and book method look like:
class Booking
{
private ConcertHall A;
public Booking(String name)
{
A = new ConcertHall(name);
}
public boolean book(String serviceClass, String name, String preference)
{
Seat seat = find(serviceClass, preference)
if(seat != null)
{
assignSeat(seat, name);
return true;
}
return false;
}
}
class ConcertHall
{
private Seat[][] VIP;
private Seat[][] Regular;
public Seat findSeat(String serviceClass, String preference)
{
if(serviceClass.equals("VIP"))
{
// query VIP array
}
else if(serviceClass.equals("Regular"))
{
// query Regular array
}
}
public boolean assignSeat(Seat seat, String name)
{
if(serviceClass.equals("VIP"))
{
// query VIP array
}
else if(serviceClass.equals("Regular"))
{
// query Regular array
}
}
}
There's already a problem here, namely for almost every method in ConcertHall, I have to do 2 identical checking for the VIP class and Regular class. Now I'm stuck. My code looks long and stupid because of the 2 identical checking.
===================================update===================================
I forgot to mention, there's an additional requirement. I have to keep track of the seats' position. Let's say a group of people want to book concert tickets, we have to find available seats in the same row. So, a HashMap wouldn't work here I think.
Factor out the identical array checking code into a method of its own and then pass it the correct lookup array. Something like
public Seat findSeat(String serviceClass, String preference)
{
if(serviceClass.equals("VIP"))
{
return findSeatIn(VIP, preference);
}
else if(serviceClass.equals("Regular"))
{
return findSeatIn(Regular, preference);
}
}
public Seat findSeatIn(Seat[][] seatArray, String preference)
{
// query seatArray for preference
}
Then repeat this with assignSeat() and assignSeatIn().
Also, findSeat() should ideally be findSeats() returning a list of seats matching a user's preference. The user should then be a able to choose one s/he likes the most, which should then be assigned by assignSeat(). Just a thought.
You can have a Map in your ConcertHall. In your check just do a get on map, query and assign. So you can do something like this
class ConcertHall{
private Map<String, Seat[][]> seatMap;
public Seat findSeat(String serviceClass, String preference){
Seat[][] seats = seatMap.get(serviceClass);
//query on seats
}
To use this, you need to put the seats in map appropriately. moreover you can use enum for the values like VIP and Regular
It seems to me that there is room (if I may say so) for an additional class that would represent individual regions of the hall; say:
class Zone {
Seat[][] seats;
public Zone(int rows, int columns) {
seats = new Seat[rows][columns];
}
public Seat findSeat(String preference) {
...
}
public boolean assignSeat(Seat seat, String name) {
...
}
}
This class could be sub-classed if different service classes would required a different behavior.
The ConcertHall class could be simpler:
class ConcertHall{
private Map<String, Zone> zoneMap = new HashMap<String, Zone>();
{
zoneMap.put("VIP", new Zone(5,10));
zoneMap.put("Regular", new Zone(50,100));
}
public Seat findSeat(String serviceClass, String preference) {
return zoneMap.get(serviceClass).findSeat(preference);
}
...
}
You need a good initial design in order to get anywhere with oop. Here is an alternate implementation you might want to consider:
class Booking {
public Booking(String personName, Seat seat) {
...
}
// getter and setters...blah blah
}
class ConcertHall {
// Making it protected allows for easy subclassing
protected List<Booking> vipSeating;
protected List<Booking> regularSeating;
public ConcertHall(String name, int capVip, int capReg) {
vip = new ArrayList<>();
regular = new ArrayList<>();
// initialise capacity, etc
}
public void addVipBooking(Booking b) throws ArrayIndexOutOfBoundsException {
// If there is room, add the person, else throw an exception
}
public void addRegularBooking(Booking b) throws ArrayIndexOutOfBoundsException {
// If there is room, add the person, else throw an exception
}
public boolean vipIsFull() {
// is the vip section full??
}
public boolean regularIsFull() {
// is the regular section full??
}
}

Categories

Resources