Consider the following question on storing values with duplicate keys:
Suppose there is a class Employee with name, sal and dob as attributes. I want to store the objects of Employee in a Map and the key would be the Employee name. The name can be duplicate.
Also after adding 10 objects in the Map. I want to retrieve the 8th object that was entered.
This is one solution to add objects with duplicate keys but for the 2nd part of the question, this would not work since on displaying the map, all values with the same key will be displayed together.
How would we maintain the order in which the objects were added in this situation? Can we modify equals and hashcode methods to somehow add the elements and then later retrieve them in the order in which they were inserted?
I think a LinkedHashMultimap (from Guava) should work for this. You wouldn't be able to get the 8th entry by index directly, but you could use something like Iterables.get(Iterable iterable, int position) to get it.
Why not just have two containers? One for mapping name to employee (like the one in the stackoverflow question you mentioned), another for mapping number to employee. You can make an "outer" container aggregating multimap and arraylist.
What you intend to do can be easily implemented using an ArrayList. This is the data structure that you should use.
The requirements are somehow contradictory. At one side several values should be possible for one key, at the other side only one value should be returned for a key. Additionaly, retrievals for a sequence should be possible. I see the nearest approximation in designing a dedicated data structure containing a hash map for fast access based on the name, and a list keeping the order of insertions. The access would be based on the overall sequence number or on the name plus index for the name. The implementation would be according the following lines:
public class Employee {
public String name; public int sal;
public Employee() {name = ""; sal = 0;}
public Employee(String name, int sal) {
this.name = name; this.sal = sal;
}
#Override public String toString() {return "(" + name + "," + sal + ")";}
}
public class Team {
private Map<String, ArrayList<Employee>> employees =
new HashMap<String, ArrayList<Employee>>();
private ArrayList<Employee> order = new ArrayList<Employee>();
public void addEmployee(Employee e) {
ArrayList<Employee> list = employees.get(e.name);
if (list == null) {
list = new ArrayList<Employee>();
employees.put(e.name, list);
}
list.add(e);
order.add(e);
}
public int getNumEmployees() {return order.size();}
public Employee getEmployee(int n) {return order.get(n - 1);}
public int getNumEmployees(String name) {
ArrayList<Employee> list = employees.get(name);
return list == null ? 0 : list.size();
}
public Employee getEmployee(String name, int n) {
ArrayList<Employee> list = employees.get(name);
return list == null ? null : list.get(n - 1);
}
}
// Test:
Team team = new Team();
team.addEmployee(new Employee("Bob", 11));
team.addEmployee(new Employee("Bob", 12));
team.addEmployee(new Employee("Eve", 13));
team.addEmployee(new Employee("Eve", 14));
System.out.println("Num all: " + team.getNumEmployees());
System.out.println("3rd: " + team.getEmployee(3));
System.out.println("Num Bobs: " + team.getNumEmployees("Bob"));
System.out.println("2nd Bob: " + team.getEmployee("Bob", 2));
Related
I need to implement a method which should take an array of persons, basically public String oldest (Person [] persons), and return the oldest one. The persons which will be inputed are the following:
new Person("Augusta Ada King, grevinna av Lovelace", 1815),
new Person("Muhammad ibn Musa al-Khwarizmi", 780),
new Person("Alan Turing", 1912),
new Person("Grace Hopper", 1906)
Below you can find my class Called Person. I've tried all different solutions with basic for-loop but I feel really lost and would appreciate any input or recommendation how I should write the method to find the oldest person.
class Person {
String name;
int yearOfBirth;
public Person(String name, int yearOfBirth) {
this.name = name;
this.yearOfBirth = yearOfBirth;
}
public int getAge() {
return getAge(java.time.LocalDate.now().getYear());
}
public int getAge(int year) {
return year - yearOfBirth;
}
#Override
public String toString() {
return String.format("%s %d", name, yearOfBirth);
}
public String oldest(Person [] persons){
}
You can try this:
Person oldest = Arrays.stream(persons).max(Comparator.comparing(Person::getAge)).get();
You need to iterate over your persons array and check which yearOfBirth is greater. You can implement your method like below:
public String oldest(Person[] persons) {
Person oldestPerson = persons[0];
for (Person person : persons) {
if (person.getYearOfBirth() < oldestPerson.getYearOfBirth()) {
oldestPerson = person;
}
}
return oldestPerson.getName();
}
This method should be static since it has nothing to do with an instance, and otherwise you have to call the method from an instance, which you probably don't want to do
Want should happen if you have more then one (oldest) person with the same age?
Why should the return be only the name and not a Person?
public static String oldest(Person[] persons) {
if (persons == null){
throw new NullPointerException("persons == null");
}
if (persons.length == 0) {
return null; //or throw same Exception depending on your handling
}
Person oldestPerson = persons[0];
for (Person person : persons) {
if (person.yearOfBirth < oldestPerson.yearOfBirth) {
oldestPerson = person;
}
}
return oldestPerson.name;
}
Since there could be the possibility of more than one oldest person I would do it as follows:
Here is some data with two oldest people (sort of)
Person[] people = {
new Person("Augusta Ada King, grevinna av Lovelace",
1815),
new Person("Muhammad ibn Musa al-Khwarizmi", 780),
new Person("Another oldest person", 780),
new Person("Alan Turing", 1912),
new Person("Grace Hopper", 1906) };
String s = Person.oldest(people);
System.out.println(s);
prints
Muhammad ibn Musa al-Khwarizmi
Another oldest person
first I would make the method static since it doesn't rely on instance fields but an array of instances.
I would use a map to facilitate holding the the names of the people using their age as the key. Use the Map.merge method to populate the map and handle duplicate ages
Now iterate thru the names and as you do so:
find the oldest age.
store the name as the value for that age.
if another is found of the same age, concatenate the name with a newline(\n) and update the current map value.
when finished, return the value for the computed oldest individual(s).
public static String oldest(Person[] persons) {
int oldest = 0;
Map<Integer, String> ages = new HashMap<>();
for (Person p : persons) {
int age = p.getAge();
oldest = Math.max(oldest, age);
ages.merge(age, p.name, (last, current)- > last + "\n" + current);
}
return ages.get(oldest);
}
In case you would rather return a List of names your method can look like this. The main differences are:
return a List<String> which contains the names
using a Map<Integer, List<String>> to contain the people based on age.
computeIfAbsent to initialize the maps value for that age one time and then add the name to the list.
public static List<String> oldest(Person[] persons) {
int oldest = 0;
Map<Integer, List<String>> ages = new HashMap<>();
for (Person p : persons) {
int age = p.getAge();
oldest = Math.max(oldest, age);
ages.computeIfAbsent(age, v->new ArrayList<>()).add(p.name);
}
return ages.get(oldest);
}
It would then be called like so
List<String> list = Person.oldest(people);
list.foreach(System.out::println); // to print - same as before.
My final recommendation is that you use Lists over Arrays as they have advantages, the main on (imho) being that they grow dynamically as you add more items.
I made a method that search through an array list of objects. Then if the searchKey is found in the array list it will print this certain item.
Here is how I iterate through the array list if it contains the searchKey, but I just realized that it is impossible to compare a string and an object.
for(int x = 0; x < Student.students.size(); x ++){
if(Student.students.contains(searchKey))
System.out.print(Student.students.get(x));
}
Here's how I create the constructor and array list.
String firstName, lastName, course, yearLevel, gender;
Student(String firstName, String lastName, String course, String yearLevel, String gender)
{
this.firstName = firstName;
this.lastName = lastName;
this.course = course;
this.yearLevel = yearLevel;
this.gender = gender;
}
static ArrayList<Student> students = new ArrayList<Student>();
You need to compare the one property; also you can use a for-each loop to simplify the code
for(Student s : Student.students){
if(s.getName().equals(searchKey))
System.out.print(s);
}
Note :
When you use a condition Student.students.contains(searchKey) in a loop, and it doesn't use the iteration variable that means there is a problem
You haven't defined what 'contains' means here but I'm going to assume that it means a Student contains the key if it appears as a substring in any of its String members. So let's start with a method that does that. We can define this as part of the Student class itself.
public class Student {
.... other stuff ....
/**
* Return true if any of the properties of this Student
* contain the given substring, false otherwise
*/
public boolean contains(String s) {
// consider addressing null cases - omitting for simplicity
return firstName.contains(s) ||
lastName.contains(s) ||
course.contains(s) ||
yearLevel.contains(s) ||
gender.contains(s);
}
}
Now you can iterate over your List and invoke this method to find the matches. Note that you need to handle the case that multiple Students may match a given search key (or none may match). So I would suggest collecting the results in a separate List. One does not generally iterate over Lists via the index. This example uses an enhanced for-loop (aka for-each).
public List<Student> findMatches(List<Student> students, String key) {
List<Student> found = new ArrayList<>();
for (Student s : students) {
if (s.contains(key)) {
found.add(s);
}
}
return found;
}
This is a good case for using the Stream API.
public List<Student> findMatches(List<Student> students, String key) {
return students.stream()
.filter(s -> s.contains(key))
.collect(Collectors.toList());
}
You already know comparing Object to String makes no sense. So, most probably what you are trying to do is check if any of the attributes of your Student object(name/course/year etc) has a value that matches your search key. To do that you need to convert your object into a String.
Add a toString method to your Student class which will look something like this:
public String toString() {
return "Student [firstName=" + firstName + ", lastName=" + lastName + ", course=" + course + ", yearLevel="
+ yearLevel + ", gender=" + gender + "]";
}
Then, look for your searchKey in the string representation of your objects while iterating.
for(int x = 0; x < students.size(); x ++){
if(students.get(x).toString().contains(searchKey))
System.out.print(students.get(x).toString());
}
Edit: As rightly pointed out by Jon Skeet in the comments, the default toString method will generate incorrect results, a custom implementation should be used to convert the object to String.
Suppose I have a class Employee :
class Employee {
Employee(String name, int age)
{
this.name = name ;
this.age = age;
}
String name ;
int age;
}
Now Create a List of this like :
ArrayList<Employee> aa = new ArrayList<Employee>();
aa.add(new Employee("Nitish", 26));
aa.add(new Employee("Sh", 2));
aa.add(new Employee("S", 1));
Can i get Employee Object Where name value is "Nitish"? Without For loop
I guess your interviewer just doesn't want you to use for or while loops to find objects in an ArrayList, but you can actually find them "without loops".
First, you need to override equals and hashCode of Employee class:
#Override public boolean equals(Object obj) {
// ...
}
#Override public int hashCode() {
// ...
}
Now you can find your object with ArrayList.indexOf (uses equals to find match) by creating a dummy reference:
Employee target = new Employee("Nitish", 26);
int index = employees.indexOf(target);
It's kinda silly but I guess some interviewers want us to think outside-of-the-box. Even though under the hood it's using loops, but if my interviewer asks me the same question, instead of saying no you can't, I'd use this example because I want to try my best just not to use "loops" as asked, and explains how it works behind the scene. Then afterwards I'd briefly come up with other better solutions, and hope that works!
You can use a filtered stream:
Object[] array = aa
.stream()
.parallel()
.filter((emp) -> emp.name.equals("Nitish"))
.toArray();
if (array.length > 0) {
Employee employee = (Employee)array[0];
String name = employee.name;
int age = employee.age;
System.out.println("Found Employee with Name = " + name + " and Age = " + age);
} else {
System.out.println("Not Found");
}
Try this
Employee emp = aa.stream().filter(e->e.name.equals("Nitish")).findAny().orElse(null);
this version returns null if not found
ArrayList work on index based structure.to ADD,READ,GET value from array need to index of the specific index.
instead store data on ArrayList you can use Map.
Using key can be access value from Map.
Map<String, Employee> employeeMap = new HashMap<>();
employeeMap.put("Nitish", new Employee("Nitish", 26));
employeeMap.put("Sh", new Employee("Sh", 2));
employeeMap.put("S", new Employee("S", 1));
System.out.println(employeeMap.get("Nitish").getName());
System.out.println(employeeMap.get("Nitish").getAge());
I Know its been asked a hundred times, and the answer is always the same, You Can not use multiple repeating values in a hashmap.
But lets get to the problem. I have an import file, the import file has information around the lines of a CustomerID, a ProductID, and Units sold (its a basic Receipt format).
What I want to do is take the import, put it into a map, and be able to reference it.
Map<integer,DoubleSales> hashmap = new HashMap <integer,DoubleSales>
try {
Scanner dataFile = new Scanner 9new File ("./salesData.csv"));
dataFile.nextLine();
while(dataFile.hasNextLine()){
String[] lineValues = line.split (",");
Integer CustomerID = Integer.parseInt(lineValues[0]);
Integer ProductID = Integer.parseInt(lineValues[1]);
integer Units = Integer.parseInt(lineValues[2]);
DoubleSales sales = new DoubleSales(CustomerID,ProductID,Units);
ProductData.put(CustomerID,sales);
}
class DoubleSales{
int CustomerID;
int ProductID;
int Units;
DoubleSales(int custID, int prodID, int Units){
CustomerID = custID;
ProductID = prodID;
Units = units;
}
}
The import file has data in the format of
CustomerID, ProductID, UnitsSold
1,10002,3
1,10004,5
1,10008,2
1,10010,3
1,10010,3
Using the code up there, When I print the customerID value of 1, I get just the last entry which is 10010,3.
How would I do it to print out, all values of CustomerID 1, and the Units sold?
for example:
1,10002,3
10004,5
10008,2
10010,3
10010,3
(will add the two 10010 values later.)
I do not wish to Use array lists.
Try MultiValueMap from Apache Common Collections.
Click here for more reference
In your case, a simple Map won't do your favor, everything you write to the value of a specified customer will be overridden, if you want to retain all entries while keeping them easily referenced, try:
First, create a structured map
Map<Integer,List<DoubleSales>> productData = new HashMap<Integer,List<DoubleSales>>();
Second, add products like this
List<DoubleSales> entries;
if(productData.get(CustomerID) == null) {
entries = new ArrayList<DoubleSales>();
entries.add(sales);
productData.put(CustomerID, entries);
} else {
List<DoubleSales> entries = productData.get(CustomerID);
entries.add(sales);
}
Third, review your products list that you just added
List<DoubleSales> products = productData.get(CustomerID);
if (products != null) {
for(DoubleSales product : products) {
// access your product here.
}
}
You have duplicated CustomerID (all having 1 as id) and you using that as a key in Hashmap. That is the reason it is keep ovverding when you insert a new record with the same id. Looks like your product id is unique. Try that or have unique customer id.
I think in that case it is better to implement the structure with a matrix. It could be done easily with arrays (or lists), where rows could contain a bean formed by the product id and the units sold, being indexed by the customer id
My first idea was Jerry Chin's solution, but I would like to show you a second approach, just to demonstrate that there are multiple solutions to the same problem.
You could store your values in a TreeSet<DoubleSales>. This would not limit the entries, you can enter for example 1,10010,3 as many times as you want.
Then, define an ordering (Comparator) on the DoubleSales, to group the orders by CustomerID.
When you print your list, you can check if the customerID of the current record is different from the prevoius record. If different, then it is the first record of the new customer. If not, it belongs to the same customer.
And the code:
SortedSet<DoubleSales> set = new TreeSet<DoubleSales>(new Comparator<DoubleSales>() {
#Override
public int compare(DoubleSales o1, DoubleSales o2) {
return Long.compare(o1.customerId, o2.customerId);
}
});
// ... import data
set.add(new DoubleSales( /*...*/ ));
// iterate through data
DoubleSales prevDS = null;
for (DoubleSales ds : set) {
if (prevDS == null || ds.customerId != prevDS.customerId) {
// first record of a customer
// print CustomerID, ProductID, UnitsSold
} else {
// second or next record of a customer
// print ProductID, UnitsSold only
}
prevDS = ds;
}
Sorry if the title isn't clear, I wasn't sure how to word it. I have an arraylist of objects and within each of these objects I store an integer value referring to a category and one referring to an ID.
I want to find the number of unique combinations of category and IDs that there are.
So at the moment I have
for(Object object: listofObjects){
//For each unique type of object.getID
//For each unique type of object.getCategory
//Add 1 to counter
}
I can't figure out how to do this. Doing things like for(int cat: object.getCategory()) brings up an error.
I can add the values to a new list within the initial for each loop like so,
ArrayList<Integer> aList= new ArrayList<Integer>();
for (Object object : spriteExplore) {
aList.add(object.getCategory());
}
for (int cat : aList) {
testCounter++;
}
but this obviosuly does not take into account uniqueness and also makes it awkward for factoring in the other variable of ID.
I feel like there is probably some easier work around that I am missing. Any advice?
Thanks in advance.
So you list of UserDefine object in ArrayList and you want to find unique Object.Just create set from list.
For e.g Suppose you have
List<Customer> list=new ArrayList<Custeomer>();
list.add(new Customer("A",12));
list.add(new Customer("B",13));
list.add(new Customer("A",12));
now
create set From this list
Set<Customer> set = new HashSet<Customer>(list);
this will have unique Customer
IMP : dont forget to override equals and hashcode method for Customer
Your best approach would be storing the data correctly.
It's possible that you still need to store non-unique items, if that's so - continue using an ArrayList, but in addition, use the following:
Override the hashcode & equels function as shown in this link:
What issues should be considered when overriding equals and hashCode in Java?
Then, use a Set (HashSet would probably be enough for you) to store all your objects. This data structure will disregard elements which are not unique to elements already inside the set.
Then, all you need to do is query the size of the set, and that gives you the amount of unique elements in the list.
I don't know any library that does this automatically, but you can do it manually using sets. Sets will retain only unique object so if you try to add the same value twice it will only keep one reference.
Set<Integer> categories = new HashSet<Integer>();
Set<Integer> ids= new HashSet<Integer>();
for (Object object : listofObjects) {
categories.add(object.getCategory());
ids.add(object.getID());
}
Then you get the number of unique categories / ids by doing
categories.size()
ids.size()
And all your unique values are stored in the sets if you want to use them.
I would look into using a (Hash)Map<Integer, Integer>. Then just have 1 foreach loop, checking to see if the value of Map<object.getId(), object.getCategory()> is null by checking if map.get(object.getId()) is null - if it is, then this pair does not exist yet, so add this pair into the map by using map.put(object.getId(), object.getCategory()). If not, do nothing. Then at the end, to find the number of unique pairs you can just use map.size()
Hope this helps
Map<Integer,List<Integer>> uniqueCombinations = new HashMap<Integer,List<Integer>>();
for (Object object : listofObjects) {
if(uniqueCombinations.get(object.getCategoryId())==null) {
uniqueCombinations.put(object.getCategoryId(), new LinkedList<Integer>);
}
uniqueCombinations.get(object.getCategoryId()).add(object.getId());
}
return uniqueCombinations.size()
I believe you want unique combinations of both category and id, right?
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class SO {
class MyObject{
private int id;
private int category;
private String name;
private MyObject(int id, int category,String name) {
super();
this.id = id;
this.category = category;
this.name = name;
}
protected int getId() {
return id;
}
protected int getCategory() {
return category;
}
#Override
public String toString() {
return "MyObject [id=" + id + ", category=" + category + ", name=" + name + "]";
}
}
public static void main(String[] args) {
SO so = new SO();
List<Object> listofObjects = new ArrayList<Object>();
listofObjects.add(so.new MyObject(1,1,"One"));
listofObjects.add(so.new MyObject(1,1,"Two"));
listofObjects.add(so.new MyObject(1,2,"Three"));
Map<String,List<MyObject>> combinations = new HashMap<String,List<MyObject>>();
for(Object object: listofObjects ){
//For each unique type of object.getID
//For each unique type of object.getCategory
//Add 1 to counter
if (object instanceof MyObject){
MyObject obj = (MyObject)object;
String unique = obj.id+"-"+obj.category;
if (combinations.get(unique) == null){
combinations.put(unique, new ArrayList<MyObject>());
}
combinations.get(unique).add(obj);
}
}
System.out.println(combinations);
//counts
for(Entry<String,List<MyObject>> entry:combinations.entrySet()){
System.out.println(entry.getKey()+"="+entry.getValue().size());
}
}
}
Use the Hashmap to save occurence. Dont forget to implement hashcode und equals Methods. You can generate them if you work with Eclipse IDE.
public static void main(String[] args) {
List<MyObject> myObjects = Arrays.asList(new MyObject(1, 2), new MyObject(2, 3), new MyObject(3, 4), new MyObject(3, 4));
Map<MyObject, Integer> map = new HashMap<>();
for (MyObject myObject : myObjects) {
Integer counter = map.get(myObject);
if(counter == null){
counter = 1;
} else {
counter = counter + 1;
}
map.put(myObject, counter);
}
long uniqueness = 0;
for(Integer i : map.values()){
if(i == 1){
++uniqueness;
}
}
System.out.println(uniqueness);
}
The last part can be replaced by this one line expression if you are working with Java 8:
long uniqueness = map.values().stream().filter(i -> i == 1).count();