I'm trying to understand a very basic concept and I'm not sure why it doesn't work.
I have two lists of type, and I want to combine them into one Set with distinct values (as it should be by the definition of Set). However, when I print the set values I get duplicates.
List<SID> list1 = (.....)
List<SID> list2 = (.....)
Set<SID> combined = new HashSet<>();
combined.addAll(list1);
combined.addAll(list2);
I also tried with distinct()
Set<SID> combinedSet = Stream.concat(list1.stream(), list2.stream()).distinct().collect(Collectors.toSet());
List<SID> combinedList = Stream.concat(list1.stream(), list2.stream()).distinct().collect(Collectors.toList());
Any idea why?
SID class overrides the hashCode and the equals method...any other way to achieve it?
Related
I have the following sample structure:
class MyObject {
private String type;
private String level;
}
Map<String,List<MyObject>> map = new HashMap<>();
MyObject myObject1 = new MyObject();
myObject1.setType("x");
myObject1.setLevel("5");
MyObject myObject2 = new MyObject();
myObject2.setType("y");
myObject2.setLevel("5");
List<MyObject> list1 = new ArrayList<>();
list1.add(myObject1);
list1.add(myObject2);
map.put("1",list1);
MyObject myObject3 = new MyObject();
myObject3.setType("x");
myObject3.setLevel("4");
List<MyObject> list2 = new ArrayList<>();
list2.add(myObject3);
map.put("2",list2);
MyObject myObject4 = new MyObject();
myObject4.setType("x");
myObject4.setLevel("5");
MyObject myObject5 = new MyObject();
myObject5.setType("y");
myObject5.setLevel("5");
List<MyObject> list3 = new ArrayList<>();
list3.add(myObject4);
list3.add(myObject5);
map.put("3",list3);
...
Based on this map, I need to create an intermediate object or some structure where I will store information about the unique values of the map. In the example above, key 1 and key 3 are the same value
so I need to store the information that the combination x = 5, y = 5 occurred twice in the map. The combination x = 4 appeared once in the map. There can be many combinations.
Any suggestions on how to do it the easiest way?
Since this looks like a homework question asking how to generally do the task I will not include code.
Think through what you have to do, write methods you'll need, implement them when you think you have all the pieces you need. Start with a stub for the method that does what you want.
The thing you can have duplicates of in the map (why are they in the map, no idea) are lists. Write a method that compares lists and returns whether they are the same.
To write that method you need a method that can compare MyObject. Best way would be to override equals() method.
Next, it'll be a question if order in the lists matters. If yes, than List equals method will work for you (read the javadoc to see exactly what it does). If not you'll need to write custom code to handle that, or sort the lists before comparison (which would involve writing a comparator for MyObject), or use a library that has that functionality (there should be something in Apache Commons).
Now that we have all that all we come back to the main method, use the ones we wrote appropriately, and all we need is do something with the results. Generally anything will do, a map with the list as key and amount of occurences as value will be simplest unless you have some more constraints or operations to do on the results.
How do I add a list of things into a set?
When I do set.addAll I get an error
required type :Collection <? extends List>
provided type :List
public static Set<List<Food>> getAllMealPlans(Set<Food> foods, int numMeals) {
Set<List<Food>> set = new HashSet<>();
List<Food> aList = new ArrayList<Food>(foods);
List<Food> sortedList = aList.stream().sorted(Comparator.comparing(a -> a.meal)).collect(Collectors.toList());
set.addAll(sortedList);
Set<List<Food>> set = new HashSet<>();
This set object is a Set of List s. This means every item in the set is a List<Food>.
How do I add a list of things into a set?
As you want to create a Set which contains multiple lists, you can simply use set.add(). This will insert the sortedList as an item in the set which will end up what you are looking for.
set.add(sortedList);
When to use addAll()?
Adds all of the specified elements to the specified collection.
Elements to be added may be specified individually or as an array.
https://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#addAll(java.util.Collection,%20T...)
Possible enhancements
As you are already using java stream, I will get rid of aList variable like below.
List<Food> sortedList = foods.stream().sorted(Comparator.comparing(a -> a.meal)).collect(Collectors.toList());
You can actually remove stream operations. First collect set items to a List object and then perform sorting with a method references in your comparator.
List foodList = new ArrayList(set);
foodList.sort(Comparator.comparing(Food::meal));
An intersection of Two Lists Objects in java 8. Can some tell me what am I doing wrong?
List<Student> originalStudent = new ArrayList<>();
List<Student> newStudent = new ArrayList<>();
List<Student> intersectListStudent = new LinkedList<>()
originalStudent.add(new Student("William", "Tyndale",1));
originalStudent.add(new Student("Jonathan", "Edwards",2));
originalStudent.add(new Student("Martin", "Luther"),3);
newStudent.add(new Student("Jonathan", "Edwards",2));
newStudent.add(new Student("James", "Tyndale",4));
newStudent.add(new Student("Roger", "Moore",5));
originalStudent.forEach(n ->
newStudent.stream()
.filter(db -> !n.getName().equals(db.getName()) &&
!n.getLastName().equals(db.getLastName()))
.forEach(student-> intersectListStudent .add(student)));
Can some tell me what am I doing wrong?
You violate the Side-effects principle of java-stream which in a nutshell says that a stream shouldn't modify another collection while performing the actions through the pipelines. I haven't tested your code, however, this is not a way you should treat streams.
How to do it better?
Simply use the List::contains in the filter's predicate to get rid of the unique values.
List<Student> students = originalStudent.stream()
.filter(newStudent::contains)
.collect(Collectors.toList());
This solution (understand the method List::contains) is based on the implemented equality comparison using Object::equals. Hence, there is needed to override the very same method in the class Student.
Edit: Please, be aware that that automatically overriding the Object::equals will mind the id to the equality computation. Therefore the equality will be based on the name and surname only. (thanks to #nullpointer).
Without the Object::equals overridden?
You have to perform the comparison in the filter using another stream and the method Stream::anyMatch which returns true if the predicate is qualified.
List<Student> students = originalStudent.stream()
.filter(os -> newStudent.stream() // filter
.anyMatch(ns -> // compare both
os.getName().equals(ns.getName() && // name
os.getLastName().equals(ns.getLastName()))) // last name
.collect(Collectors.toList());
What you can do is construct a SortedSet<Student> from the two concatenated lists originalStudent and newStudent. The sorted set uses a Comparator.comparing(Student::getName).thenComparing(Student::getLastName) as its comparator.
Stream.concat(originalStudent.stream(), newStudent.stream())
.collect(Collectors.toCollection(() -> new TreeSet<>(
Comparator.comparing(Student::getFname)
.thenComparing(Student::getLname))
))
I have an entity named Elementfisa, which contains as values (id,Post,Sarcina). Now, Post(Int Id,String Nume,String Tip) and Sarcina(Int Id,String Desc) are also entities. I have a List of all the elements I added as Elementfisa, and I want to get in a separate list the frequency of every Sarcina that every Elementfisa contains. This is my code right now:
int nr=0;
List<Integer> frecv=new ArrayList<Integer>();
List<Sarcina> sarcini = new ArrayList<>();
List<Elementfisa> efuri=findEFAll();
for (Elementfisa i : efuri)
{
nr=0;
for (Sarcina s : sarcini)
if (s.equals(i.getSarcina()))
nr=1;
if (nr==0)
{
int freq = Collections.frequency(efuri, i.getSarcina());
sarcini.add(i.getSarcina());
frecv.add(freq);
}
}
(findEFAll() returns every element contained in a Hashmap from a repository)
But for some reason, while the sarcini list contains all the Sarcina from every Elementfisa, the frequency list will show 0 on every position. What should I change so every position should show the correct number of occurrences?
You're using Collections.frequency() on efuri, a List<Elementfisa>. But you're passing i.getSarcina() to it, a Sarcina object. A List of Elementfisa cannot possibly contain a Sarcina object, so you get zero. You may have passed the wrong list to the method.
Edit:
To look at all Sarcinas in efuri, you can do this using Java 8 streams:
efuri.stream().map(element -> element.getSarcina())
.collect(Collectors.toList()).contains(i.getSarcina())
Breakdown:
efuri.stream() //Turns this into a stream of Elementfisa
.map(element -> element.getSarcina()) //Turns this into a stream of Sarcina
.collect(Collectors.toList()) //Turn this into a list
.contains(i.getSarcina()) //Check if the list contains the Sarcina
Are you sure you do not need to override equals() of Elementisa? (and hashcode() too). The default Java equals() does not seem to get what you want because it would be checking the identity (not the value) of two Elementisa objects, while in your logic, two such objects with the same values may be considered as equivalent.
For more information on equals(), see
What issues should be considered when overriding equals and hashCode in Java?
I would like to get unique values from two Collection objects. How would I do that?
Example: Let us take two ArrayLists:
List bag1 = new ArrayList();
List bag2 = new ArrayList();
bag1.add("1");
bag1.add("2");
bag1.add("3");
bag1.add("7");
bag1.add("8");
bag1.add("9");
bag2.add("4");
bag2.add("5");
bag2.add("6");
bag2.add("7");
bag2.add("8");
bag2.add("9");
I need to get a result like --> 1,2,3 from bag1 and 4,5,6 from bag2
Could you please help me out?
Two things:
Use org.apache.commons.collections.CollectionUtils.disjunction(Collection a, Collection b);
Bag isn't the best variable name for a list. :)
You should take a look at Sets instead. The Java Collection has a few classes which deal with this. The idea is you could just the the set difference between the two collections, and you'll get your answer.
Use a 'set' to store your data. That way your collection will have unique elements as and when you add elements to the set.
See the javadoc over here: http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Set.html
have you tried...
bag1.removeAll(bag2);
If you want to keep bag1 and bag2 intact you can use a Set variable and pass all values of bag1 into the Set and then check for contains() like
Set set = new HashSet();
set.addAll(bag2);
for(Object o: bag1){
if(!set.contains(o)){
// Do whatever you want with bag1 elements
}
}
set.clear();
set.addAll(bag1);
for(Object o: bag2){
if(!set.contains(o)){
// Do whatever you want with bag2 elements
}
}
Use the removeAll method define in the Set interface.
Set intersect = new TreeSet(bag1);
intersect.removeAll(bag2);
List unique1 = Arrays.asList(intersect);
intersect = new TreeSet(bag2);
intersect.removeAll(bag1);
List unique2 = Arrays.asList(intersect);