Search in ArrayList using Comparator - java

I had an interview today and I have given two java classes and asked to search dog details by registration number. I know that Java.util.ArrayList.contains(Object) but do not know how to implement when there are more than one fields.
The second question was: what is the most efficient search techniques you can use in this example? I thought about Collections.binarySearch but not sure that it is the most efficient in this example. If so, how can I implement it?
DogSort.java
public class DogSort {
public static void main(String[] args) {
ArrayList<Dog> listDog = new ArrayList<Dog>();
Scanner sc = new Scanner(System.in);
listDog.add(new Dog("Max", "German Shepherd", "33"));
listDog.add(new Dog("Gracie","Rottweiler","11"));
Collections.sort(listDog, Dog.COMPARE_BY_NAME);
System.out.println(listDog);
}
}
Dog.java
class Dog {
private String name;
private String breed;
private String registrationNumber;
public Dog(String name, String breed, String registrationNumber) {
this.name = name;
this.breed = breed;
this.registrationNumber = registrationNumber;
}
public static Comparator<Dog> COMPARE_BY_NAME = new Comparator<Dog>() {
public int compare(Dog one, Dog other) {
return one.name.compareTo(other.name);
}
};
//getter and setter methods for all private variable
}

I agree with #Pritam Banerjee's answer. The most efficient search technique is to use HashMap in this scenario. I would recommend to use HashSet but the HashSet#contains method returns boolean, so just use map. Here is the code snippet.
Just for Information When using hash based collection/map dont forget to implement hashCode and equals method properly.
public class DogSearch {
public static void main(String[] args) {
Map<String, Dog> dogs = new HashMap<String, Dog>();
Dog max = new Dog("Max", "German Shepherd", "1001");
Dog gracie = new Dog("Gracie", "Rottweiler", "1002");
Dog luca = new Dog("Luca", "Labrador", "1003");
Dog tiger = new Dog("Tiger", "Beagle", "1004");
Dog meemo = new Dog("Meemo", "Bulldog", "1005");
Dog lacie = new Dog("Lacie", "German Shorthaired Pointer", "1006");
dogs.put(max.getRegistrationNumber(), max);
dogs.put(gracie.getRegistrationNumber(), gracie);
dogs.put(luca.getRegistrationNumber(), luca);
dogs.put(tiger.getRegistrationNumber(), tiger);
dogs.put(meemo.getRegistrationNumber(), meemo);
dogs.put(lacie.getRegistrationNumber(), lacie);
Dog result = dogs.get("1002");
if (result == null) {
System.out.println("Dog not found");
} else {
System.out.println(result);
}
}
}
class Dog {
private String name;
private String breed;
private String registrationNumber;
public Dog(String name, String breed, String registrationNumber) {
this.name = name;
this.breed = breed;
this.registrationNumber = registrationNumber;
}
public static Comparator<Dog> COMPARE_BY_NAME = new Comparator<Dog>() {
public int compare(Dog one, Dog other) {
return one.name.compareTo(other.name);
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
public String getRegistrationNumber() {
return registrationNumber;
}
public void setRegistrationNumber(String registrationNumber) {
this.registrationNumber = registrationNumber;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((breed == null) ? 0 : breed.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((registrationNumber == null) ? 0 : registrationNumber.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Dog other = (Dog) obj;
if (breed == null) {
if (other.breed != null)
return false;
} else if (!breed.equals(other.breed))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (registrationNumber == null) {
if (other.registrationNumber != null)
return false;
} else if (!registrationNumber.equals(other.registrationNumber))
return false;
return true;
}
#Override
public String toString() {
return "Dog [name=" + name + ", breed=" + breed + ", registrationNumber=" + registrationNumber + "]";
}
}
Time Complexity
Insertion : O(1)
Search : O(1)

Add it to a HashMap with Registration as Key and Dog object as the value and then search the Map.
O(1) insertion and O(1) searching.
Binary Search O(log n).

For the first question ie to search details by registration number here is the code
import java.util.Comparator;
import java.util.TreeMap;
public class DogSort {
public static void main(String[] args) {
TreeMap<Integer, Dog> listDogs = new TreeMap<>();
listDogs.put(33, new Dog("Max", "German Shepherd", "33"));
listDogs.put(11, new Dog("Gracie", "Rottweiler", "11"));
System.out.println(listDogs);
System.out.println(listDogs.containsKey(11));
System.out.println(listDogs.get(11));
}
}
class Dog {
private String name;
private String breed;
private String registrationNumber;
public Dog(String name, String breed, String registrationNumber) {
this.name = name;
this.breed = breed;
this.registrationNumber = registrationNumber;
}
public static Comparator<Dog> COMPARE_BY_NAME = new Comparator<Dog>() {
public int compare(Dog one, Dog other) {
return one.name.compareTo(other.name);
}
};
#Override
public String toString() {
return "Dog [name=" + name + ", breed=" + breed + ", registrationNumber=" + registrationNumber + "]";
}
}
It is very difficult to get the details of dog by registration number using arraylist, but with map it is quite easy.
And you can override the hashcode and equals method like this but the arraylist compare method works differently.
What you can do is you can write a method which can search details by registration number. The method will have to iterate the list and find the Dog object,and if the list is sorted then you need to implement your own binary search to get the Dog object according to the registration number.
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class DogSort {
public static void main(String[] args) {
ArrayList<Dog> listDog = new ArrayList<Dog>();
listDog.add(new Dog("Max", "German Shepherd", "33"));
listDog.add(new Dog("Gracie", "Rottweiler", "11"));
Collections.sort(listDog, Dog.COMPARE_BY_NAME);
System.out.println(listDog);
System.out.println(listDog.contains(new Dog("Max", "Rottweiler", "33")));
}
}
class Dog {
private String name;
private String breed;
private String registrationNumber;
public Dog(String name, String breed, String registrationNumber) {
this.name = name;
this.breed = breed;
this.registrationNumber = registrationNumber;
}
public static Comparator<Dog> COMPARE_BY_NAME = new Comparator<Dog>() {
public int compare(Dog one, Dog other) {
return one.name.compareTo(other.name);
}
};
#Override
public String toString() {
return "Dog [name=" + name + "]";
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((registrationNumber == null) ? 0 : registrationNumber.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Dog other = (Dog) obj;
if (registrationNumber == null) {
if (other.registrationNumber != null)
return false;
} else if (!registrationNumber.equals(other.registrationNumber))
return false;
return true;
}
}

Related

Overridden equals and hashCode does not work on custom Object;

I have an below object
class CustomObj{
private String name;
private String dept;
public String getName(){
return this.name;
}
public String getDept(){
return this.dept;
}
private CustomObj(){
}
private CustomObj(CustomObjBuilder builder){
this.name = builder.name;
this.dept= builder.dept;
}
#Override
public boolean equals(Object o) {
if (o == this) return true;
if (o == null || getClass() != o.getClass()) return false;
CustomObj that = (CustomObj) o;
return that.name.equals(name) &&
that.dept.equals(dept);
}
#Override
public int hashCode() {
int result = 31;
result = 31 * result + name.hashCode();
result = 31 * result + dept.hashCode();
return result;
}
public static class CustomObjBuilder{
private String name;
private String dept;
public CustomObjBuilder(String name, String dept){
this.name = name;
this.dept = dept;
}
public CustomObjBuilder setName(String name){
this.name = name;
return this;
}
public CustomObjBuilder setDept(String dept){
this.dept = dept;
return this;
}
public CustomObj build(){
return new CustomObj(this);
}
}
}
and class that uses above
class XYZ{
Set<CustomObj> obj = new HashSet<CustomObj>();
public void process(String a, String b){
CustomObj o = new CustomObj.CustomObjBuilder(a,b).build();
if(!obj.contains(o)){
obj.add(o);
}
}
}
And a test class
class TestXYX{
#Test
public void test(){
XYZ xyz = new XYZ();
xyz.process("TEST","TESTABC");
xyz.process("TEST","TESTABC");
}
}
Beacuse I have overrideen hascode and equals, both the above are equal and when process is called second time, the control should not go into if(!obj.contains(o)) second time and size of the set should be 1. But when i run the test obj.add(o); is called two times. But the values of both this object and that objec inside equals methods are same, but
that.name.equals(name) && that.dept.equals(dept)
inside CustomObj returns false. Can someone please help me understand why?
The code is fine. To verify add an sysout to check Set size:
class XYZ {
Set<CustomObj> obj = new HashSet<CustomObj>();
public void process(String a, String b) {
CustomObj o = new CustomObj.CustomObjBuilder(a, b).build();
if (!obj.contains(o)) { // Fails second time for your use case.
obj.add(o);
}
System.out.println(obj.size()); // This is 1 in your use case.
}
}

Stackoverflow exception (Recursion) for typical 1-to-n relationship

This is my Car class:
public class Car {
private int FGNr;
private String name;
private String type;
private Owner owner;
private static ArrayList<Integer> allCarIds = new ArrayList<>();
public Car(int FGNr, String name, String type, Owner o) throws Exception {
setFGNr(FGNr);
setName(name);
setType(type);
setOwner(o);
}
public int getFGNr() {
return FGNr;
}
public void setFGNr(int FGNr) throws Exception{
this.FGNr = FGNr;
if(allCarIds.contains(this.FGNr))
throw new Exception("FGNr already excists!! ");
allCarIds.add(this.FGNr);}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Owner getOwner() {
return owner;
}
public void setOwner(Owner owner) throws Exception{
owner.addCar(this);
this.owner = owner;
}
#Override
public int hashCode() {
int hash = 7;
hash = 73 * hash + this.FGNr;
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Car other = (Car) obj;
if (this.FGNr != other.FGNr) {
return false;
}
return true;
}
#Override
public String toString() {
return "Car{" + "FGNr=" + FGNr + ", name=" + name + ", type=" + type + ", owner=" + owner + '}';
}
}
And this is my Owner class:
public class Owner {
private String SVNr;
private String name;
HashSet<Car> allCars = new HashSet<>();
private static ArrayList<String> allOwnerSVNs = new ArrayList<>();
public Owner(String SVNr, String name) throws Exception{
setSVNr(SVNr);
setName(name);
}
public void addCar(Car c) throws Exception{
if(allCars.contains(c))
throw new Exception("this user has already this car");
if(c.getOwner()!=null)
throw new Exception("this car belongs to other owner");
c.setOwner(this);
allCars.add(c);
}
public String getSVNr() {
return SVNr;
}
public void setSVNr(String SVNr) throws Exception{
this.SVNr = SVNr;
if(allOwnerSVNs.contains(this.SVNr))
throw new Exception("SVNg already excists!! ");
allOwnerSVNs.add(this.SVNr);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HashSet<Car> getAllCars() {
return allCars;
}
#Override
public int hashCode() {
int hash = 5;
hash = 41 * hash + Objects.hashCode(this.SVNr);
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Owner other = (Owner) obj;
if (!Objects.equals(this.SVNr, other.SVNr)) {
return false;
}
return true;
}
#Override
public String toString() {
return "Owner{" + "SVNr=" + SVNr + ", name=" + name + ", allCars=" + allCars + '}';
}
}
And this is my main:
try {
Owner o1 = new Owner("0001","Owner1");
Owner o2 = new Owner("0002","Owner2");
Car c1 = new Car(1,"Model S", "Tesla",o1);
Car c2 = new Car(2,"Model 3", "Tesla",o2);
Car c3 = new Car(3,"TT", "Audi",o2);
} catch (Exception ex) {
System.out.println("error:"+ex.getMessage());
}
So when trying to create a new Car I get this error:
Exception in thread "main" java.lang.StackOverflowError
at java.util.HashMap.containsKey(HashMap.java:595)
at java.util.HashSet.contains(HashSet.java:203)
at pkgData.Owner.addCar(Owner.java:28)
at pkgData.Car.setOwner(Car.java:63)
...........
It is a recursion error, but I don't know how to fix it. If I create a new car obviously I have to add the Car to the owner arrayList of cars. and if I call the addCar function the function calls the getOwner function. It's an endless circle of calling.
How I can make sure that when creating a new car that the collection of the owner will also be changed. It would not make any sense that a car has an owner but the owner of the car does not the car in his collection.
These two functions fall an infinite loop as you see.
In Car class
public void setOwner(Owner owner) throws Exception{
owner.addCar(this);
this.owner = owner;
}
And in Owner Class
public void addCar(Car c) throws Exception{
if(allCars.contains(c))
throw new Exception("this user has already this car");
if(c.getOwner()!=this && c.getOwner()!=null)
throw new Exception("this car belongs to other owner");
c.setOwner(this);
allCars.add(c);
}
The car sets its owner and sends itself to the Owner class' addCar() method, thats OK. However, why the Owner class' addCar() method sets the owner as itself again ?
I think there is a logical mistake. If you remove c.setOwner(this) line, it works fine.
Usually adding element to a container should be done in the container itself.
In you example Owner is the container and Car is the element.
As an example see java.awt.Container and java.awt.Component.
Don't call owner.addCar(this); from the Car.setOwner.
Let the Owner add the car to the list (which you already do) and set itself as owner to the Car.
public void setOwner(Owner owner) throws Exception{
owner.addCar(this); //remove this line
this.owner = owner;
}

cqengine cant index by equals

I'm trying to add an index where my override equals() determines if two objects are the same or not.
Car.java
public static class Car {
final String id;
private String name;
public Car(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static final Attribute<Car, Car> CAR = new SimpleAttribute<Car, Car>() {
#Override
public Car getValue(Car car, QueryOptions queryOptions) {
return car;
}
};
#Override
public String toString() {
return "Car{" + "id=" + id + ", name=" + name + '}';
}
}
Fetcher.java
public static final ResultSet<Car> get(final IndexedCollection<Car> indexedCollection, final Car car) {
return indexedCollection.retrieve(QueryFactory.equal(Car.CAR, car));
}
Main.java
public static void main(String args[]) {
IndexedCollection<Car> cars = new ConcurrentIndexedCollection<>();
cars.addIndex(NavigableIndex.onAttribute(Car.CAR));
}
The problem is on this line cars.addIndex(NavigableIndex.onAttribute(Car.CAR)); where the error message is no suitable method found for onAttribute(Attribute<Car,Car>). Am I doing something wrong here or is there another call I should be using instead?
Remove cars.addIndex(NavigableIndex.onAttribute(Car.CAR));, because it is not really an usefull index... and I think this was not a motivation of the developer. You should create Attributes for CAR_ID and CAR_NAME and create an Query for comparison. In this case I misuse (to achieve what you expect) IndexedCollection as a simple Set. But... here is a possible solution, if I have understood you correctly:
Override equals in Car:
class Car {
private final int id;
private String name;
public Car(int i, String name) {
this.id = i;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(obj == null) return false;
if (!(obj instanceof Car)) return false;
Car car = (Car) obj;
if(car.getId() == this.getId())
if(car.getName().equals(this.getName()))
return true;
return false;
}
public static final Attribute<Car, Car> CAR = new SimpleAttribute<Car, Car>() {
#Override
public Car getValue(Car car, QueryOptions queryOptions) {
return car;
}
};
#Override
public String toString() {
return "Car{" + "id=" + id + ", name=" + name + '}';
}
}
Main:
IndexedCollection<Car> cars = new ConcurrentIndexedCollection<>();
cars.add(new Car(1, "test"));
cars.add(new Car(2, "test2"));
cars.add(new Car(3, "test3"));
Car s = new Car(2, "test2");
ResultSet<Car> cs= cars.retrieve(QueryFactory.equal(Car.CAR, s));
cs.forEach(c -> System.out.println(c.toString()));

Delete element of a table in a method

What i wanna do is to remove an object from a table of object ONLY IF the object i wanna delete have the id i put in params.
Let's get into the code :
public static animals[] supprimerAnimals(int identifiant, animals[] liste){
animals[] newOne = new animals[0];
for(int i = 0; i < liste.length; i++){
}
return newOne;
}
This method will receive a id and a table in params.
In the table, we have objects... let's tell them animals. Here is a list of objects we could have :
liste[0] = animals(1, "cat", 6)
liste[1] = animals(2, "dog", 4)
here would be the constructor :
animals(int id, String type, int age);
So we have all we would need to get the solution.
So now let's get into an example...
If i do this :
animals[] zoo = supprimerAnimals(2, liste);
I need that zoo contains this :
zoo[0] = animals(1, "cat", 6);
Can you guys put me on the right way please ?
I'm getting lock on the fact that i have to create a new table and i don't even now if the id will exist on the old table... So i can't fix the size of the new table...
Thank you guys
animals[]
means that there is a java Object called animals, and the items in that array have this DataType "animals"
bellow i created an Object named Animal and here is an example with 2 ways of doing that
public class Animal{
private int id;
private String name;
private int age;
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;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
now some where you can implement the deleteMethods something like:
public static Animal[] removeAnimal(Animal[] animals, int id) {
List<Animal> list = Arrays.asList(animals);
for(Animal item : animals)
if( item.getId() == id )
list.remove(item);
return list.toArray(animals);
}
OR
public static Animal[] removeAnimal2(Animal[] animals, int id) {
Animal[] arr = new Animal[animals.length-1];
int i = 0;
for(Animal item : animals){
if( item.getId() != id ){
try{
arr[i]=item;
}catch(Exception ex){
ex.printStackTrace();
}
}
}
return arr;
}
See if this suits your need.
public static animals[] supprimerAnimals(int identifiant, animals[] liste) {
int supprimer = -1;
for (int i = 0; i < liste.length; i++) {
if (liste[i].getId() == identifiant) {
supprimer = i;
break;
}
}
if (supprimer < 0) {
return liste;
}
animals[] nouveauListe = new animals[liste.length - 1];
for (int i = 0; i < supprimer; i++) {
nouveauListe[i] = liste[i];
}
for (int i = supprimer + 1; i < liste.length; i++) {
nouveauListe[i - 1] = liste[i];
}
return nouveauListe;
}
Note
Java class names by convention are PascalCase and are singular, not plural
Just Check out this. I have used list instead of Arrays.
package com.Hangman;
import java.util.ArrayList;
import java.util.List;
public class Test5 {
public static void main(String[] args) {
List zoo= new ArrayList();
Animal animal= new Animal(1,"cat",01);
zoo.add(animal);
animal= new Animal(6,"dog",02);
zoo.add(animal);
animal= new Animal(3,"mouse",03);
zoo.add(animal);
animal= new Animal(8,"rabbit",04);
zoo.add(animal);
animal= new Animal(5,"lion",05);
zoo.add(animal);
System.out.println("List of Animals in zoo");
System.out.println(zoo);
System.out.println("Animal to be removed from zoo is having id 3");
removeFromZoo(zoo, 3);
System.out.println("Animals in zoo after removal");
System.out.println(zoo);
}
static void removeFromZoo(List zoo,int id){
Animal animal= new Animal(id,null, 0);
zoo.remove(animal);
}
}
class Animal{
int id;
String type;
int age;
public Animal(int id, String type, int age) {
super();
this.id = id;
this.type = type;
this.age = age;
}
#Override
public String toString() {
return ""+this.type;
}
#Override
public boolean equals(Object animal) {
if(animal instanceof Animal){
return this.id==((Animal) animal).id;
}else
return false;
}
}

Strange program behaviour

This elementary program is driving me up the wall.
There must be something very simple I don't see here.
WHY is the exception triggered?
There are 2 classes:
1)
public class Person
{
private String name;
private int age;
private static int numberOfPeople = 0;
public Person()
{
this("John Doe", 0);
numberOfPeople++;
}
public Person(String name, int age)
{
this.setAge(age);
this.setName(name);
numberOfPeople++;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public int getNumberOfPersons()
{
return numberOfPeople;
}
public String toString()
{
return this.name + " " + this.age;
}
}
2)
import java.util.Random;
public class Adult extends Person
{
private String number;
public static final int MIN_AGE = 18;
public Adult(String name, int age, String number)
{
super(name, 0);
this.setAge(age);
this.number = number;
}
public Adult(Adult adult)
{
this(adult.getName(), adult.getAge(), adult.getNumber());
}
public Adult()
{
this.number = "";
this.setAge(MIN_AGE);
Random rand = new Random();
int result = rand.nextInt(2);
if (result == 0)
{
this.setName("John Doe");
}
else
{
this.setName("Jane Doe");
}
}
public void setAge(int age)
{
if (age < MIN_AGE)
{
throw new IllegalArgumentException("The person must be 18 or older!");
}
else
{
super.setAge(MIN_AGE);
}
}
public String getNumber()
{
return this.number;
}
private void setNumber(String number)
{
this.number = number;
}
public String toString()
{
return this.getName() + " " + this.getNumber() + " " + this.getAge();
}
public boolean equals(Object obj)
{
boolean result = false;
if (obj != null && this.getClass() == obj.getClass())
{
Adult other = (Adult) obj;
if (this.getName().equals(other.getName()) &&
this.getNumber().equals(other.getNumber()) &&
this.getAge() == other.getAge())
{
result = true;
}
}
return result;
}
public static void main(String[] args)
{
Adult ad = new Adult();
System.out.println(ad);
}
}
This gives my the following error:
Exception in thread "main" java.lang.IllegalArgumentException: The person must be 18 or older!
at people.Adult.setAge(Adult.java:39)
at people.Person.<init>(Person.java:16)
at people.Adult.<init>(Adult.java:12)
at people.Adult.main(Adult.java:75)
Your Person() constructor creates another person. Since Adult extends Person, there is an implicit super() call which is likely the cause of your error.

Categories

Resources