Collection list is returning duplicates - java

Hey I am implementing this function.
private static HashMap<String, Set<String>> enrollments = new HashMap<String, Set<String>>();
private static Set<String> studentset;
/**
* Enrolls a student into a unit.
*
* #param unit
* #param student
*/
public static void enroll(String unit, String student) {
if(!enrollments.containsKey(unit)) {
studentset = new HashSet<String>();
}
studentset.add(student);
enrollments.put(unit, studentset);
}
/**
* Gets a list of all students of a particular discipline. E.g. If discipline is
* "ABC" then return a collection of all students enrolled in units that start
* with "ABC", so ABC301, ABC299, ABC741 etc. This method is non-trivial so it
* would help to first implement the helper method matchesDiscipline (below).
*
* #param discipline
* #return
*/
public static Set<String> getStudents(String discipline) {
Set<String> myList = new HashSet<String>();
for (Entry<String, Set<String>> e : enrollments.entrySet()) {
if (e.getKey().startsWith(discipline)) {
myList.addAll(e.getValue());
}
}
return myList;
}
public static void main(String[] args) {
EnrollmentManager.enroll("CAB302", "James");
EnrollmentManager.enroll("CAB403", "Ben");
EnrollmentManager.enroll("CAB302", "James");
EnrollmentManager.enroll("CAB403", "Morgan");
EnrollmentManager.enroll("CAB404", "Sam");
System.out.println(EnrollmentManager.getStudents("CAB3"));
}
The problem im having is 'myList' is outputting [Morgan, James, Ben]. Where the correct answer would be [James]. Where am I going wrong? Sorry if its a simple solution im new to Collections.

The problem with your code is that you are using a static studentSet variable. Let me dry run your code.
What you are doing here is that you are creating a new set when key is not found, otherwise you are using existing set, which is your static member.
if(!enrollments.containsKey(unit)) {
studentset = new HashSet<String>();
}
EnrollmentManager.enroll("CAB302", "James"); will create a new set, let's say set1 and assign it to static member studentset. studentset after this step {James}.
EnrollmentManager.enroll("CAB403", "Ben"); will create a new set, let's say set2 and assign it to static member studentset. studentset after this step {Ben}
EnrollmentManager.enroll("CAB302", "James"); will NOT create a new set and use current value of studentset i.e. set2. studentset after this step {Ben,James}
EnrollmentManager.enroll("CAB403", "Morgan"); will NOT create a new set and use current value of studentset i.e. set2. studentset after this step {Ben,James,Morgan}.
And since your map has a reference to this static member, whenever you retrieve , you are getting the static member values..
What you should do is instead of using the static set member, you can use a function variable.
And change your condition like this:
Hashset<String> studentset = null;
if(!enrollments.containsKey(unit)) {
studentset = new HashSet<String>();
}else{
studentset = enrollments.containsKey(unit);
}

You can change your enroll method like below to work:
public static void enroll(String unit, String student) {
if(!enrollments.containsKey(unit)) {
studentset = new HashSet<String>();
enrollments.put(unit, studentset);
} else {
studentset = enrollments.get(unit);
}
studentset.add(student);
}
Your mistake: you have a static reference private static Set<String> studentset; which in your case is used to add a new student in the previous entry of map when !enrollments.containsKey(unit) returns false. Best practice is to make this a local variable and define it in enroll method.

Problem is with your static variable studentset and you are adding to that set even if there is studentset already in the map for a unit. You should add to the already existing set in the map if a key is found.
If you are using Java 8 or higher you can simply do it in one line :
enrollments.computeIfAbsent(unit, k -> new HashSet<>()).add(student);
And you don't need a studentset static field at all.

Related

Having trouble accessing and referring to HashMap in Java

I'm trying to write a program that handles the storing of students and corresponding subjects, but am relatively new to Java's Lists and am having trouble getting the class to store and output the students and subjects. So far I can get it to store a student and class but will not take multiple, or only returns the most recent one added.
private static Map<String, Set<String>> cohort = new HashMap<~>();
public static void signOn(String class, String student) {
Set<String> studentSet = new HashSet<String>();
studentSet.add(student);
cohort.put(class, studentSet);
}
public static Map<String, Set<String>> getCohort() {
return cohort;
}
When calling getCohort() I am trying to get it to return all students that have signed on, but it is only returning the most recent student added in. I'm not sure if I am missing something simple but I just can't seem to get it right, any help would be very appreciated.
in signOn when you are adding a new student, you are creating a new Set, adding one student to it and then overrides any Set that was already there. This means that you will always only have one student there.
What you need to do is to first get the current set of students in a class, and then add your student to them.
Example code:
public static void signOn(String class, String student)
{
Set<String> studentSet = cohort.get(class);
if (studentSet == null) {
studentSet = new HashSet<String>();
cohort.put(class, studentSet);
}
studentSet.add(student);
}
That should work as studentSet will be a reference to the studentSet that is stored in cohort. I just wrote that from memory so no promises it works in the first try, but that is the general idea.
You are always creating a new Set, thus overriding the already existing set you saved in cohort.
Try the following:
Set<String> studentSet = cohort.get(class);
if(studentSet == null){
studentSet = new HashSet<String>();
cohort.put(class, studentSet);
}
studentSet.add(student);
As a side note I would like to add that 'class' is properly not the best name for your String variable as it's a Java reserved word.
You are facing the problem as you are putting new Set every time (also when student class is the same), which basically override the old value. So, you need to put a student in the same set if the class is the same. Using Java 8 you can do as follows :-
public static void signOn(String cls, String student) {
cohort.computeIfAbsent(cls, k -> {
Set<String> studentSet = new HashSet<>();
studentSet.add(student);
return set;
});
cohort.computeIfPresent(cls, (k, v) -> {
v.add(student);
return v;
});
}
Note: You can't have a variable name class, it is a reserved keyword in java.

How to call a HashSet method in separate main class

I'm trying to do a Java problem that's noted in the textbook "Building Java Programs" 4th Ed. The problem is number 11 in Chapter 11, Page 751:
Write a method called symmetricSetDifference that accepts two sets as parameters and returns a new Set containing their symmetric difference(that is, the set of elements contained in either of the two sets, but not in both) For an example: The difference between the sets[1,4,7,9] and [2,4,5,6,7] is [1,2,5,6,9].
The symmetricSetDifference method:
public static Set<Integer>symmetricSetDifference(Set<Integer>list1, Set<Integer>list2) {
Set<Integer>set1 = new HashSet<>();
set1.add(1);
set1.add(4);
set1.add(7);
set1.add(9);
Set<Integer>set2 = new HashSet<>();
set2.add(2);
set2.add(4);
set2.add(5);
set2.add(6);
set2.add(7);
Set<Integer>diff = new HashSet<>(set1);
diff.addAll(set2);
Set<Integer>curr = new HashSet<>(set1);
curr.retainAll(set2);
diff.removeAll(curr);
System.out.println(diff);
return diff;
}
This is the main class. It's in a separate file:
public class TestPointClass {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
newSet = MyUtils.symmetricSetDifference(List1, List2);
}
The problem is that I get an "illegal expression" error along with a series of "cannot find the identifier errors. I was wondering if anyone has any pointers on what I can do? (I've checked for any duplicate issues and could not find anything similar, thus apologies in advance if there were)
do the setup of your both Sets (set1,set2) outside the method and add them as parameter:
Set<Integer>set1 = new HashSet<>();
set1.add(1);
...
Set<Integer>set2 = new HashSet<>();
set2.add(2);
...
MyUtils.symmetricSetDifference(set1 , set2 );
if you want to use the return type you have to do it like that:
Set<Integer> result = MyUtils.symmetricSetDifference(set1 , set2 );

MAPS. Java. Return of values

I am struggling with following:
I need to create a method which returns a collection of all values that specify some particular selection criterion specified by one or more arguments.
My MAP consists of PPS numbers(keys) and values( town, name, surname, place of work ) Both are strings .
However, I am not sure what I need to do to get the values after placin in the map.
/**
*This method returns a collection of all people who work for CO-OP
*/
public Set<String> selectKeys(String factory)
{
for (Set<String>eachTaxPayers : taxPayersList.values())
{
if(taxPayersList.values().contains(factory))
{
Set<String>eachClients = taxPayersList.keySet();
System.out.println(taxPayersList.keySet());
}
}
return null ;
}
Could someone help me please?
This is a code how Map is populated.
public class Tax
{
Map<String, Set<String>>taxPayersList;
public Tax()
{
taxPayersList = new HashMap<>();
Set<String>taxPayersDetails = new HashSet<>();
taxPayersDetails.add(" Eddie Donegan");
taxPayersDetails.add("Prodieco");
taxPayersDetails.add("Limerick");
taxPayersList.put("4481908A", taxPayersDetails);
taxPayersDetails = new HashSet<>();
taxPayersDetails.add(" Paddy Power");
taxPayersDetails.add("Covenant");
taxPayersDetails.add("Limerick");
taxPayersList.put("6088989B", taxPayersDetails);
taxPayersDetails = new HashSet<>();
taxPayersDetails.add(" Mikey Obama");
taxPayersDetails.add("Prodieco");
taxPayersDetails.add("Limerick");
taxPayersList.put("6788910B", taxPayersDetails);
}
}
I want only to return the key's( PPS numbers) for people who works for the same company
public Set<String> selectKeys(String factory) {
// our return Set
Set<String> factoryWorkers = new HashSet<>();
// Let's iterate over the map entries
for (Map.Entry entry : taxPayersList.entrySet()) {
// Let's grab the value of the current map entruy
Set<String> eachTaxPayers = entry.getValue()
// Risky move
if(eachTaxPayers.contains(factory)) {
// add the key (PPS) to the return set
factoryWorkers.add(entry.getKey());
}
}
return factoryWorkers;
}
FYI, the line marked as "Risky Move" is not the best approach.
Though unlikely, it's possible a city has the same name as factory.
You'd be better using an Iterator on the Set and comparing against the 2nd value.
Even better, instead of having a Map>
you could have a Map
where Employee has fields such as name, city and factoryName.

Avoiding printing of duplicates in ArrayList

I have one array list contains object( id,name).need to remove if duplicate names in the list
I need to print only name once if it is repeating
Use SET if you want don't want duplicate entries in your list:
HashSet if you don't required sequence
LinkedHashSet if you need to print name in sequence
Note: You must override equals and hashcode in object(ID,Name) otherwise you will find duplicate object in SET
public static boolean checkDuplicate(ArrayList list) {
HashSet set = new HashSet();
for (int i = 0; i < list.size(); i++) {
boolean val = set.add(list.get(i));
if (val == false) {
return val;
}
}
return true;
}
Use a java.util.Set instead. It cannot contain duplicates.
Reference: http://docs.oracle.com/javase/6/docs/api/java/util/Set.html
Set<String> myNames = new HashSet<String>();
myNames.add("JAMES");
myNames.add("ELLA");
myNames.add("JAMES");
myNames.size() // will return 2
Use a Set data structure with implementing appropriate Comparator. Run a loop and put your objects in that set. Print this set.
Arraylist provides an iterator that allows to delete the last element returned by the iterator from the list itself. So we just keep track of the names that we've already seen. A Set implementation is helpful.
Set<String> names = new HashSet<>();
Iterator<MyType> it = getListFromSomewhere().iterator(); // you know how
while(it.hasNext()) {
if (!names.add(it.next().getName()) {
it.remove();
}
}
Hint: adding to a set returns false, if the value is already in the set.
Remarks
Your requirement wasn't that clear: here we keep the first entry with a name and delete all following entries from the list. If the list is not sorted by id, then it is rather random, which entries are kept an which are removed.
You can do it with the help of Set as well as List family
We have seen how to remove duplicates with Set family. We can do it with the help of List family as well. Please refer below code.
package com.rais.util;
import java.util.ArrayList;
import java.util.List;
/**
* #author Rais.Alam
* #project Utils
* #date Dec 18, 2012
*/
public class ConverterUtil
{
/**
* #param args
*/
public static void main(String[] args)
{
List<String> original = new ArrayList<String>();
original.add("Tomcat");
original.add("Jboss");
original.add("GlassFish");
original.add("Weblogic");
original.add("Tomcat");
List<String> newList = removeDuplicates(original);
for (String value : newList)
{
System.out.println(value);
}
}
public static List<String> removeDuplicates(List<String> original)
{
List<String> tempList = new ArrayList<String>();
for (String value : original)
{
if (!tempList.contains(value))
{
tempList.add(value);
}
}
return tempList;
}
}

Combining two HashMaps into a third

Let's say I have an object of each class below, and I put each object in a hashmap where IDnumber is the key in both maps.
class1 {
int IDNumber = 123; //same person as class2
String name = John;
String company = Intel;
class2 {
int IDNumber = 123; //same person as class1
int income = 500;
int workYears = 3;
}
HashMap<Integer, class1> one = new Hashmap<Integer, class1>();
HashMap<Integer, class2> two = new HashMap<Integer, class2>();
Now, how can I mash these two HashMaps into a third HashMap so that I can have the key IDnumber, and the values name, company, income, and workyears?
You can not do that. You have two different classes, and java is not going to auto-magically make them one.
You could create a new third class to merge the info:
public Class3 {
public Class3(Class1 class1, Class2 class2){
//pull the info you want from each into variables in this class
}
}
Then loop through your map to get the entries, creating new Class3 instance for each and place them in a new HashMap<Integer, Class3>.
//gets the keys from the hashmap
Set<Integer> keys = one.keySet();
//merge the keys from the second hashmap
keys.addAll(two.keySet());
//new hashmap
Map<Integer, Class3> newMap = new HashMap<Integer, Class3>();
for (Integer key : keys){
//create new instance and place it in the map
newMap.put(key, new Class3(one.get(key), two.get(key));
}
Create a 3rd class called Combined in one of these 2 ways:
Combined {
class1 info1;
class2 info2;
}
Or, better:
Combined {
int IDNumber = 123;
String name = John;
String company = Intel;
int income = 500;
int workYears = 3;
}
Create a 3rd (empty) HashMap
Now iterate over all elements in the first HashMap you had before
For each entry, look up the same key in the 2nd HashMap:
If it is found, combine the information from these 2 entries and add it as a single entry of instance Combined to the 3rd HashMap. Then remove both of these entries from both HashMaps one and two.
If it is not found, then create a Combined instance anyway based on the entry in HashMap one and just set the unavailable information that would have come from a corresponding entry from HashMap two to null. Remove the entry from HashMap one.
Now the 1st HashMap should be empty. Iterate HashMap two to find any entries that did not have a corresponding ID in HashMap one. Add them to the 3rd HashMap as above.
You can't store multiple values (i.e. Class1 and Class2 in your case) with the same key in a java.util.Map. What you want is a Multimap. Guava has an implementation for this. The one you are looking for is ArrayListMultimap.
You need to make a new class which is a mashup of class1 and class2 along with a new Map for it. Each time you put something in one and 'two' you make a new mashup containing both objects and put it into three.
As your data structure is not solit you should use some adapter/wraper
public class UserClassWrapper {
private final UserClass1 class1;
private final UserClass2 class2;
UserWithDetail(UserClass1 class1, UserClass2 class2) {
//Check preconditions as class1 and class2 must not be null and have equal id
this.class1 = class1;
this.class2 = class2;
}
}
Then in your code you can declare a map:
private Map<Integer,UserClassWrapper> userClassWrappeMap = new HashMap<>();
You can use an ArrayList to create an HashMap with multiple of values for one key
ArrayList<> list = new ArrayList();
/* populate your list here with two class having the same ID */
HashMap<Integer, List> three = new Hashmap();
/* Put list on the Hashmap */
/* Redo the operation with another ID */
But it's not very optimized, if you aren't obliged to have an HashMap at final, use directly a multiHashMap: http://commons.apache.org/collections/apidocs/org/apache/commons/collections/MultiHashMap.html
Are you expecting this kind of arrangment
class class1 {
int IDNumber = 123; //same person as class2
String name = "John";
String company = "Intel";
}
class class2 {
int IDNumber = 123; //same person as class1
int income = 500;
int workYears = 3;
}
public class MyData{
public static void main(String arg[]){
HashMap<Integer, class1> one = new HashMap<Integer, class1>();
HashMap<Integer, class2> two = new HashMap<Integer, class2>();
one.put(1, new class1());
two.put(2, new class2());
HashMap<Integer, Object> three = new HashMap<Integer, Object>();
three.putAll(one);
three.putAll(two);
System.out.println(three);
}
}

Categories

Resources