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 );
Related
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.
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.
Considering the code below and the fact that the 4 HashSets are populated elsewhere.
My aim is to contain all element(s) that are common in all 4 HashSets.
My question is that first of all, am I doing it right? Secondly, if I'm doing it right, is there a better way to do it? If not, then what solution do I have for this problem?
static Set<String> one=new HashSet<>();
static Set<String> two=new HashSet<>();
static Set<String> three=new HashSet<>();
static Set<String> four=new HashSet<>();
private static void createIntersectionQrels() {
ArrayList<String> temp = new ArrayList<>();
Set<String> interQrels = new HashSet<>();
temp.addAll(one);
one.retainAll(two);
interQrels.addAll(one);
one.addAll(temp);
one.retainAll(three);
interQrels.addAll(one);
one.addAll(temp);
one.retainAll(four);
interQrels.addAll(one);
one.addAll(temp);
interQrels.retainAll(two);
interQrels.retainAll(three);
interQrels.retainAll(four);
}
I think you can simply can call retainAll() on the first set, using the second, third, and fourth sets as parameters:
private static Set<String> getIntersectionSet() {
// create a deep copy of one (in case you don't wish to modify it)
Set<String> interQrels = new HashSet<>(one);
interQrels.retainAll(two); // intersection with two (and one)
interQrels.retainAll(three); // intersection with three (and two, one)
interQrels.retainAll(four); // intersection four (and three, two, one)
return interQrels;
}
I'm a bit new to Java 8, but this seems pretty readable:
Set<String> intersection = one.stream()
.filter(two::contains)
.filter(three::contains)
.filter(four::contains)
.collect(Collectors.toSet());
Here's a quick Junit test to try out:
#Test
public void testIntersectionBetweenSets() {
Collection<String> one = new HashSet<>(4);
one.add("Larry");
one.add("Mark");
one.add("Henry");
one.add("Andrew");
Set<String> two = new HashSet<>(2);
two.add("Mark");
two.add("Andrew");
Set<String> three = new HashSet<>(3);
three.add("Mark");
three.add("Mary");
three.add("Andrew");
Set<String> four = new HashSet<>(3);
four.add("Mark");
four.add("John");
four.add("Andrew");
Set<String> intersection = one.stream()
.filter(two::contains)
.filter(three::contains)
.filter(four::contains)
.collect(Collectors.toSet());
Collection<String> expected = new HashSet<>(2);
expected.add("Andrew");
expected.add("Mark");
Assert.assertEquals(expected, intersection);
}
I would think the best way to handle this is with Groovy. I know you didn't ask for groovy, but anytime I can convert all that code into one line, it's hard to resist.
println one.intersect(two).intersect(three).intersect(four)
I am currently working on one of the usecases where you are given 6 strings which has 3 oldValues and 3 newValues like given below:
String oldFirstName = "Yogend"
String oldLastName = "Jos"
String oldUserName = "YNJos"
String newFirstName = "Yogendra"
String newLastName ="Joshi"
String newUserName = "YNJoshi"
now what I basically want to do is compare each of the oldValue with its corresponding new value and return true if they are not equal i.e
if(!oldFirstName.equalsIgnoreCase(newFirstName)) {
return true;
}
Now, since I am having 3 fields and it could very well happen that in future we might have more Strings with old and new value I am looking for an optimum solution which could work in all cases no matter how many old and new values are added and without having gazillions of if else clauses.
One possibility I thought was of having Old values as OldArrayList and new values as newArraylist and then use removeAll where it would remove the duplicate values but that is not working in some cases.
Can anyone on stack help me out with some pointers on how to optimum way get this done.
Thanks,
Yogendra N Joshi
you can use lambdaj (download here,website) and hamcrest (download here,website), this libraries are very powerfull for managing collections, the following code is very simple and works perfectly:
import static ch.lambdaj.Lambda.filter;
import static ch.lambdaj.Lambda.having;
import static ch.lambdaj.Lambda.on;
import static org.hamcrest.Matchers.isIn;
import java.util.Arrays;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> oldNames = Arrays.asList("nameA","nameE","nameC","namec","NameC");
List<String> newNames = Arrays.asList("nameB","nameD","nameC","nameE");
List<String> newList = filter(having(on(String.class), isIn(oldNames)),newNames);
System.out.print(newList);
//print nameC, nameE
}
}
With this libraries you can solve your problem in one line. You must add to your project: hamcrest-all-1.3.jar and lambdaj-2.4.jar Hope this help serve.
NOTE: This will help you assuming you can have alternatives to your code.
You can use two HashMap<yourFieldName, yourFieldValue> instead of two Arrays / Lists / Sets of Strings (or multiple random Strings);
Then you need a method to compare each value of both maps by their keys;
The result will be an HashMap<String,Boolean> containing the name of each field key, and true if the value is equal in both maps, while false if it is different.
No matter how many fields you will add in the future, the method won't change, while the result will.
Running Example: https://ideone.com/dIaYsK
Code
private static Map<String,Boolean> scanForDifferences(Map<String,Object> mapOne,
Map<String,Object> mapTwo){
Map<String,Boolean> retMap = new HashMap<String,Boolean>();
Iterator<Map.Entry<String, Object>> it = mapOne.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,Object> entry = (Map.Entry<String,Object>)it.next();
if (mapTwo.get(entry.getKey()).equals(entry.getValue()))
retMap.put(entry.getKey(), new Boolean(Boolean.TRUE));
else
retMap.put(entry.getKey(), new Boolean(Boolean.FALSE));
it.remove(); // prevent ConcurrentModificationException
}
return retMap;
}
Test Case Input
Map<String,Object> oldMap = new HashMap<String,Object>();
Map<String,Object> newMap = new HashMap<String,Object>();
oldMap.put("initials","Y. J.");
oldMap.put("firstName","Yogend");
oldMap.put("lastName","Jos");
oldMap.put("userName","YNJos");
oldMap.put("age","33");
newMap.put("initials","Y. J.");
newMap.put("firstName","Yogendra");
newMap.put("lastName","Joshi");
newMap.put("userName","YNJoshi");
newMap.put("age","33");
Test Case Run
Map<String,Boolean> diffMap = Main.scanForDifferences(oldMap, newMap);
Iterator<Map.Entry<String, Boolean>> it = diffMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,Boolean> entry = (Map.Entry<String,Boolean>)it.next();
System.out.println("Field [" + entry.getKey() +"] is " +
(entry.getValue()?"NOT ":"") + "different" );
}
You should check too if a value is present in one map and not in another one.
You could return an ENUM instead of a Boolean with something like EQUAL, DIFFERENT, NOT PRESENT ...
You should convert your String to some Set.
One set for OLD and another for NEW. And your goal of varity number of elements will also be resolved using same.
As it's set order of it will be same.
I have a void method similar to this
public void handle(MyObject myObject) {
Set<String> mySet = new HashSet<String>();
mySet.addAll(myObject.getStuff();
...
mySet.addAll(someOtherList);
...
myService.doSomething(mySet);
}
I am using Mockito. I have mocked myService and I am verifying that myService.doSomething(mySet) gets called with any Set like this:
verify(myService).doSomething(any(Set.class));
But I want to verify the expected contents of the set. I tried something like this:
Set<String> expected = new HashSet<String>();
expected.add("item1");
expected.add("item2");
verify(myService).doSomething(expected);
This verified failed because due to "Argument(s) are different" as expected was [item1,item2] while actual was [item2,item1]. So the content is the same but not in the correct order.
How can I check that the contents are the same without changing the collection being used that maintains order?
I wonder if you've simplified your example too much? Two Set instances will be equal provided that they contain the same objects, regardless of order.
See my example test below, which passes.
#Test
public void test() throws Exception {
Foo foo = mock(Foo.class);
Set<String> set = new HashSet<>();
set.add("a");
set.add("b");
foo.doSomething(set);
Set<String> set2 = new HashSet<>();
set2.add("b");
set2.add("a");
verify(foo).doSomething(set2);
}
public interface Foo {
void doSomething(Set<String> strings);
}