Ive been searching SO about this question and most only have the problem with two arrays comparing by have a nested loop. My problem is quite the same but on a bigger scale. Suppose I have a 100 or thousand user on my app, and each user has the list of item it wants.
Something like this
User1 = {apple,orange,guava,melon,durian}
User2 = {apple, melon,banana,lemon,mango}
User3 = {orange,carrots,guava,melon,tomato}
User4 = {mango,carrots,tomato,apple,durian}
.
.
Nuser = ...
I wanted to see how many apples or oranges was listed from all the users array. So I am basically comparing but on a bigger scale. The data isn't static as well, A user can input an unknown fruit from the developers knowledge but on the users knowledge they can put it there so there can be multiple users that can put this unknown fruit and yet the system can still figure out how many is this unknown item was listed. Keep in mind this is a dynamic one. User can reach for example a 100 users depending how popular an app would be. I can't afford to do nested loop here.
PS this is not the exact problem but it is the simplest scenario I can think of to explain my problem.
PS: just to clarify, I dont intend to use 3rd party lib as well like guava. I am having a problem on proguard with it.
Edit
Just read that Original poster cannot use Java 8, which is a pity, because this would realy make it very easy!
Java 7 solution
final Map<String, Integer> occurencesByFruit = new HashMap<>();
for (User user : users) {
String[] fruits = user.getFruits();
for (String fruit : fruits) {
final Integer currentCount = occurencesByFruit.get(fruit);
if (currentCount == null) {
occurencesByFruit.put(fruit, 1);
} else {
occurencesByFruit.put(fruit, currentCount + 1);
}
}
}
Java 8 solution
I'd stream the users, flatMap() to the actual fruit elements, and then use Collectors.groupingBy() with a downstream collector Collectors.counting().
This will give you a Map where the keys are the fruits, and the values are the occurrences of each fruit throughout all your users.
List<User> users = Arrays.asList(/* ... */);
final Map<String, Long> occurencesByFruit = users.stream()
.map(User::getFruits)
.flatMap(Arrays::stream)
.collect(Collectors.groupingBy(f -> f, Collectors.counting()));
Seems it is a good possibility to use HashMap<Item, Integer> fruits. You could iterate over all Users (you would need to store all Users in some kind of list, such as ArrayList<User> users) and check the list of items chosen by each User (I suppose User should have a field ArrayList<Item> items in its body to store items). You could achieve it with something like that:
for (User user : users) { // for each User from users list
for (Item item : user.items) { // check each item chosen by this user
if (fruits.containsKey(item) { // if the fruit is already present in the items HashMap increment the amount of items
int previousNumberOfItems = fruits.get(item);
fruits.put(item, ++previousNumberOfItems);
else { // otherwise put the first occurrency of this item
fruits.put(item, 1);
}
}
}
I would either create an ArrayList containing a HashMap with strings and ints or use two ArrayLists (one of type String and one of type Integer). Then you can iterate over every entry in each of the user arrays (this is only a simple nested loop). For every entry in the current user array you check if there is already the same entry in the ArrayList you created additionally. If so, you increment the respective int. If not, you add a string and an int. In the end, you have the number of occurrences of all the fruit strings in the added ArrayLists, which is, if I understood you correctly, just what you wanted.
Related
So I'm going crazy with this one. This is for an assignment and can't seem to get this to work at all!!
I have the following HashMap:
HashMap<String, ArrayList<Team>> teams;
(Team being another class to obtain the details of the teams)
What I need to be able to do is get the List of teams for the Key(String) from the above HashMap, and assign the List to a local variable I have declared:
List<Team> results = teams.get(division);
But this is where I get stuck. I have no idea how I'm suppose to complete this task.
As a further note "division" is the Key used in the HashMap. The ArrayList is a list of teams that belong to the division.
I have tried the below, which does not compile at all. Really not sure how I can get this to work!!
public void recordResult(String division, String teamA, String teamB, int teamAScore, int teamBScore)
{
List<Team> results = teams.get(division);
for (String i : teams.keySet())
{
results = new ArrayList<Team>();
results.add();
}
}
**You can ignore the arguments after the "String division". These will be used later.
Iterate over the entrySet() of the Map. Now you can fetch each List for that specific key and proceed further. Something like:
for (Entry<String, ArrayList<Team>> entry : teams.entrySet()) {
// extract the value from the key using `teams.get(entry.getKey())`
// proceed further with the value obtained
}
I need to return a List, or a Collection in general, that gives me the 10 taxpayers who spent the most in the entire system. The classes are divided in User, Taxpayer (which extends User) and Expense, and in my main class Main I have a Map holding every single value for Users and Expenses, respectively a Map<String, User> users and a Map<String, Expense> expenses.
The first step would be to go through the Map of users and check if it's a Taxpayer , then for that Taxpayer get all the Expenses he has done. Inside each expense there's a variable called Value with a getValue method to return the Value.
I've tried to do it but I was having a problem in updating the Collection if the next Taxpayer had a higher sum on Expense values than the one on the "end" of the Collection.
Also, I would prefer if this wasn't done in Java 8 since I'm not very comfortable with it and there's more conditions that I would need to set in the middle of the method.
Edit (what I have until now):
public List<Taxpayer> getTenTaxpayers(){
List<taxpayer> list = new ArrayList<Taxpayer>();
for(User u: this.users.values()){
if(!u.getUserType()){ // if it is a Taxpayer
Taxpayer t = (Taxpayer) u;
double sum = 0;
for(Expense e: this.expenses.values()){
if(t.getNIF().equals(e.getNIFClient())){ //NIF is the code that corresponds to the Taxpayer. If the expense belongs to this Taxpayer, enters the if statement.
sum += e.getValue();
if(list.size()<10){
list.add(t.clone());
}
}
}
}
}
}
So if I understand correctly, when you already have 10 Taxpayers in your list, you are struggling on how to then add another taxpayer to the list to maintain a only to top 10 "spenders"
One way to approach this is to gather the expenses of all your Taxpayers and add them all to your list. Then sort the list in reverse order by the amount they have spent. Then just get the first 10 entries from the list.
You could do this using the Collections.sort() method defining your own custom Comparator
Something like:
List<Taxpayer> taxpayers =...
Collections.sort(taxpayers, new Comparator<Taxpayer>()
{
#Override
public int compare(Taxpayer o1, Taxpayer o2)
{
return o1.sum - o2.sum; // using your correct total spent here
// or to just sort in reverse order
// return o2.sum - o1.sum;
}
});
Or if Taxpayer implements Comparable you can just use
Collections.sort(taxpayers)
Then reverse
Collections.reverse(taxpayers)
Then get top 10
List<Taxpayer> top10 = taxpayers.subList(0, 10);
To be more efficient though you could just define the comparator to sort the list in reverse order - then you don't need to reverse the list - just get the top 10.
I have a bunch of objects stored in hashMap<Long,Person> i need to find the person object with a specific attribute without knowing its ID.
for example the person class:
public person{
long id;
String firstName;
String lastName;
String userName;
String password;
String address;
..
(around 7-10 attributes in total)
}
lets say i want to find the object with username = "mike". Is there any method to find it without actually iterating on the whole hash map like this :
for (Map.Entry<Long,Person> entry : map.entrySet()) {
if(entry.getValue().getUserName().equalsIgnoreCase("mike"));
the answers i found here was pretty old.
If you want speed and are always looking for one specific attribute, your best bet is to create another 'cache' hash-map keyed with that attribute.
The memory taken up will be insignificant for less than a million entries and the hash-map lookup will be much much faster than any other solution.
Alternatively you could put all search attributes into a single map (ie. names, and ids). Prefix the keys with something unique if you're concerned with collisions. Something like:
String ID_PREFIX = "^!^ID^!^";
String USERNAME_PREFIX = "^!^USERNAME^!^";
String FIRSTNAME_PREFIX = "^!^FIRSTNAME^!^";
Map<String,Person> personMap = new HashMap<String,Person>();
//add a person
void addPersonToMap(Person person)
{
personMap.put(ID_PREFIX+person.id, person);
personMap.put(USERNAME_PREFIX+person.username, person);
personMap.put(FIRSTNAME_PREFIX+person.firstname, person);
}
//search person
Person findPersonByID(long id)
{
return personMap.get(ID_PREFIX+id);
}
Person findPersonByUsername(String username)
{
return personMap.get(USERNAME_PREFIX+username);
}
//or a more generic version:
//Person foundPerson = findPersonByAttribute(FIRSTNAME_PREFIX, "mike");
Person findPersonByAttribute(String attr, String attr_value)
{
return personMap.get(attr+attr_value);
}
The above assumes that each attribute is unique amongst all the Persons. This might be true for ID and username, but the question specifies firstname=mike which is unlikely to be unique.
In that case you want to abstract with a list, so it would be more like this:
Map<String,List<Person>> personMap = new HashMap<String,List<Person>>();
//add a person
void addPersonToMap(Person person)
{
insertPersonIntoMap(ID_PREFIX+person.id, person);
insertPersonIntoMap(USERNAME_PREFIX+person.username, person);
insertPersonIntoMap(FIRSTNAME_PREFIX+person.firstname, person);
}
//note that List contains no duplicates, so can be called multiple times for the same person.
void insertPersonIntoMap(String key, Person person)
{
List<Person> personsList = personMap.get(key);
if(personsList==null)
personsList = new ArrayList<Person>();
personsList.add(person);
personMap.put(key,personsList);
}
//we know id is unique, so we can just get the only person in the list
Person findPersonByID(long id)
{
List<Person> personList = personMap.get(ID_PREFIX+id);
if(personList!=null)
return personList.get(0);
return null;
}
//get list of persons with firstname
List<Person> findPersonsByFirstName(String firstname)
{
return personMap.get(FIRSTNAME_PREFIX+firstname);
}
At that point you're really getting into a grab-bag design but still very efficient if you're not expecting millions of entries.
The best performance-wise method I can think of is to have another HashMap, with the key being the attribute you want to search for, and the value being a list of objects.
For your example this would be HashMap<String, List<Person>>, with the key being the username. The downside is that you have to maintain two maps.
Note: I've used a List<Person> as the value because we cannot guarantee that username is unique among all users. The same applies for any other field.
For example, to add a Person to this new map you could do:
Map<String, List<Person>> peopleByUsername = new HashMap<>();
// ...
Person p = ...;
peopleByUsername.computeIfAbsent(
p.getUsername(),
k -> new ArrayList<>())
.add(p);
Then, to return all people whose username is i.e. joesmith:
List<Person> matching = peopleByUsername.get("joesmith");
Getting one or a few entries from a volatile map
If the map you're operating on can change often and you only want to get a few entries then iterating over the map's entries is ok since you'd need space and time to build other structures or sort the data as well.
Getting many entries from a volatile map
If you need to get many entries from that map you might get better performance by either sorting the entries first (e.g. build a list and sort that) and then using binary search. Alternatively you could build an intermediate map that uses the attribute(s) you need to search for as its key.
Note, however, that both approaches at least need time so this only yields better performance when you're looking for many entries.
Getting entries multiple times from a "persistent" map
If your map and its valuies doesn't change (or not that often) you could maintain a map attribute -> person. This would mean some effort for the initial setup and updating the additional map (unless your data doesn't change) as well as some memory overhead but speeds up lookups tremendously later on. This is a worthwhile approach when you'd do very little "writes" compared to how often you do lookups and if you can spare the memory overhead (depends on how big those maps would be and how much memory you have to spare).
Consider one hashmap per alternate key.
This will have "high" setup cost,
but will result in quick retrieval by alternate key.
Setup the hashmap using the Long key value.
Run through the hashmap Person objects and create a second hashmap (HashMap<String, Person>) for which username is the key.
Perhaps, fill both hashmaps at the same time.
In your case,
you will end up with something like HashMap<Long, Person> idKeyedMap and HashMap<String, Person> usernameKeyedMap.
You can also put all the key values in the same map,
if you define the map as Map<Object, Person>.
Then,
when you add the
(id, person) pair,
you need to also add the (username, person) pair.
Caveat, this is not a great technique.
What is the best way to solve the problem?
There are many ways to tackle this as you can see in the answers and comments.
How is the Map is being used (and perhaps how it is created). If the Map is built from a select statement with the long id value from a column from a table we might think we should use HashMap<Long, Person>.
Another way to look at the problem is to consider usernames should also be unique (i.e. no two persons should ever share the same username). So instead create the map as a HashMap<String, Person>. With username as the key and the Person object as the value.
Using the latter:
Map<String, Person> users = new HashMap<>();
users = retrieveUsersFromDatabase(); // perform db select and build map
String username = "mike";
users.get(username).
This will be the fastest way to retrieve the object you want to find in a Map containing Person objects as its values.
You can simply convert Hashmap to List using:
List list = new ArrayList(map.values());
Now, you can iterate through the list object easily. This way you can search Hashmap values on any property of Person class not just limiting to firstname.
Only downside is you will end up creating a list object. But using stream api you can further improve code to convert Hashmap to list and iterate in single operation saving space and improved performance with parallel streams.
Sorting and finding of value object can be done by designing and using an appropriate Comparator class.
Comparator Class : Designing a Comparator with respect to a specific attribute can be done as follows:
class UserComparator implements Comparator<Person>{
#Override
public int compare(Person p1, Person p2) {
return p1.userName.compareTo(p2.userName);
}
}
Usage : Comparator designed above can be used as follows:
HashMap<Long, Person> personMap = new HashMap<Long, Person>();
.
.
.
ArrayList<Person> pAL = new ArrayList<Person>(personMap.values()); //create list of values
Collections.sort(pAL,new UserComparator()); // sort the list using comparator
Person p = new Person(); // create a dummy object
p.userName="mike"; // Only set the username
int i= Collections.binarySearch(pAL,p,new UserComparator()); // search the list using comparator
if(i>=0){
Person p1 = pAL.get(Collections.binarySearch(pAL,p,new UserComparator())); //Obtain object if username is present
}else{
System.out.println("Insertion point: "+ i); // Returns a negative value if username is not present
}
Let's suppose I've an object that looks like this:
public class Supermarket {
public String supermarketId;
public String lastItemBoughtId;
// ...
}
and I have two lists of supermarkets, one "old", another "new" (i.e. one is local, the other is retrieved from the cloud).
List<Supermarket> local = getFromLocal();
List<Supermarket> cloud = getFromCloud();
I would like to find all the pairs of Supermarket objects (given supermarketId) that have lastItemBoughtId different from one another.
The first solution I have in mind is iterating the first List, then inside the first iteration iterating the second one, and each time that local.get(i).supermarketId.equals(cloud.get(j).supermarketId), checking if lastItemBoughtId of the i element is different from the id of the j element. If it's different, I add the whole Supermarket object on a new list.
To be clearer, something like this:
List<Supermarket> difference = new ArrayList<>();
for (Supermarket localSupermarket : local) {
for (Supermarket cloudSupermarket : cloud) {
if (localSupermarket.supermarketId.equals(cloudSupermarket.supermarketId) &&
!localSupermarket.lastItemBoughtId.equals(cloudSupermarket.lastItemBoughtId))
difference.add(cloudSupermarket);
}
}
Clearly this looks greatly inefficient. Is there a better way to handle such a situation?
One solution :
Construct a Map of the Local supermarkets using the supermarketId as the key by running through the list once
Loop through the cloud list and do you comparison, looking up the local supermarket from your map.
i.e. O(n) instead of O(n2)
Here's a two-line solution:
Map<String, Supermarket> map = getFromLocal().stream()
.collect(Collectors.toMap(s -> s.supermarketId, s -> s));
List<Supermarket> hasDiffLastItem = getFromCloud().stream()
.filter(s -> !map.get(s.supermarketId).lastItemBoughtId.equals(s.lastItemBoughtId))
.collect(Collectors.toList());
I would put one of the lists in a Map with as key the Supermarket ID and as value the supermarket instance then iterate over the other getting from the Map and comparing the lastItemBoughtId.
I am having an option in my website for the user i.e: "Settings" in that I given 3 options(TextBoxes) to enter details: 1.E-mail, 2.SMS, 3.MMS.. in this user can enter another mail id: its an optional thing but, if he enter the both or same which is neccesary e-mail and optional or same then, I have to tell that "given e-mail" alredy exist.
I am sending this data as ArrayList that to coverted as JSON object.
What is the best way to find the duplicate and notify that to user
Help me in this
Thanks in advance
Either parse it into Java collections with a JSON framework of your choice, then check for duplicates or use JavaScript to directly work on the JSON.
If you have the ArrayList anyway, why don't iterate over that?
Please do the following
HashSet hashSet = new HashSet(arrayList1);
ArrayList arrayList2 = new ArrayList(hashSet) ;
if(arrayList2.size()<arrayList1.size()){
//duplicates exits
}
You can do what Ammu posted, but this will not identify the duplicate entry. If you have the ArrayList as a Java object (if not, convert it into one), convert the ArrayList into a HashSet, compare the size to identify if there are duplicate entries. If so, you need to sort the ArrayList in order to find the duplicate entry.
Collections.sort(arrayList);
for(int i = 1; i < arrayList.size() - 1; i++){
if (arrayList.get(i).equals(arrayList.get(i - 1))){
// found duplicate
System.out.println("Duplicate!");
}
}
this works only if the entries of the ArrayList implement the sortable interface. But since your ArrayList is filled with strings this is the case.
Based on what you described
"... in this user can enter another
mail id: its an optional thing but, if
he enter the both or same which is
neccesary e-mail and optional or same
then, I have to tell that "given
e-mail" alredy exist."
I would alert the user using Javascript and avoid the HTTP Request/Response round-trip to the server:
...
// before submitting the form
if (document.getElementById('requiredEmail').value == document.getElementById('optionalEmail').value) {
alert("The optional email must be different than the required email");
}
...
As suggested before by other user, you can just create a Set based on the ArrayList if you are validating the input in the backend...
String[] parsedInput = new String[] { "SMS-Value", "MMS-Value", "email#domain.com", "email#domain.com" }
List<String> receivedList = Arrays.asList(parsedInput);
Set<String> validatedList = new HashSet<String>(receivedList);
if (validatedList.size() < receivedList.size()) {
throw new IllegalArgumentException("The email addresses provided are incorrect.");
}
If you want to find the duplicates then you can iterate over the list and find.
like:
Map<Object, Integer> map = new HashMap<Object, Integer>();
for(Object obj : list)
{
if(map.containsKey(obj))
{
map.put(obj, map.get(obj)+1);
}
else
{
map.put(obj, 1);
}
}
Objects in the map having value more than 1 are duplicate.
If you just want to get rid of duplicates (rather than knowing which are actually duplicates)
Ex:
Set set = new HashSet(list);
set can not have duplicate elements, so it will remove all duplicates.