Have I found a bug in java.util.ArrayList.containsAll? - java

In Java I've two lists:
List<Satellite> sats = new ArrayList<Satellite>();
List<Satellite> sats2 = new ArrayList<Satellite>();
Satellite sat1 = new Satellite();
Satellite sat2 = new Satellite();
sats.add(sat1);
sats2.add(sat1);
sats2.add(sat2);
When I do the following containsAll method on the first list:
sats.containsAll(sats2); //Returns TRUE!
It returns true. But the first List (sats) only contains 1 item and the second list contains 2. Therefor it's not even possible that the first list (sats) containsAll items from the second list (sats2). Any idea why or is this a bug in the Java JDK?
I've read in another StackOverflow question that this is not the most performant way to do something like this, so if anyone has a suggestion on how to make it more performant that would be great!
Thanks in advance!

As pointed out by #Progman, you're probably overriding the equals method in Satellite.
The program below prints false.
import java.util.*;
class Satellite {
}
class Test {
public static void main(String[] args) {
List<Satellite> sats = new ArrayList<Satellite>();
List<Satellite> sats2 = new ArrayList<Satellite>();
Satellite sat1 = new Satellite();
Satellite sat2 = new Satellite();
sats.add(sat1);
sats2.add(sat1);
sats2.add(sat2);
System.out.println(sats.containsAll(sats2));
}
}
(ideone.com demo)
I suggest that you print the contents of the two lists and check that the content corresponds to what you expect it to be.

For many classes, it makes sense that two objects created the same way (e.g. new Satellite()) would be considered equal. Keep in mind that containsAll doesn't care about the number of copies of an object that a Collection contains, just that it contains at least one of each distinct element in the Collection that it's given. So for example, if you had a List a that contained [A, A] and a list b that just contained [A], b.containsAll(a) and a.containsAll(b) would both return true. This is probably analogous to what's happening here.

Too late to reply but the second part of your question - a more efficient way to do containsAll is : CollectionUtils.isSubCollection(subSet, superSet)
That is O(n^2) vs O(n) complexity

Related

Functional Programing; using streams to create a set/ list of objects in java

the object of this specific function is to create a list of (competitor objects ) Using 2 lists containing time and names.
note that this is a trail to convert from object oriented to functional programing , so its actually a record rather than an object.
my initial trial using a sort of recursion
public static List<Competitors> makeObjects(int[] time, String[] name){
if (counter > name.length){
return racers;
}else{
Competitors racer= new Competitors(name[counter],backToTime(time[counter]));
racers.add(racer);
return makeObjects(++counter,racers,time,name);
}
and my try after discovering i can use streams
public static List<Competitors> makeObjects(int[] time, String[] name){
List<Competitors> racers = new ArrayList<Competitors>();
IntStream.range(0,time.length).mapToObj(i-> ).collect(new Competitors(name[i] ,backToTime(time[i])))
}
Your second approach, makeObjects(), is the functional approach.
Where you go wrong is that you are creating the Competitor objects in the wrong place; you map each i to a new Competitor. Then in the collect() call, you specify what type of collection. In your case, it would be Collectors.toList() or Collectors.toSet(). There is no need to create an ArrayList; just assign the IntStream... to List<Competitor> racers.
Note that you should probably guard this method by first asserting that the arrays are the same length.
Also note that your class should be named Competitor, not Competitors; and makeObjects would be more descriptive as createCompetitorsList.

Creating an ArrayList inside a recursive method

I'm attempting to solve a problem that finds the amount of valid solutions to a chess problem and when the code is printed, it prints 92 arrays of an object PartialSolution I made that are the correct solutions. This is done recursively and I need to add these arrays to an array list but I can't figure out how.
Here's my code:
public ArrayList<PartialSolution> solve(PartialSolution sol ){
ArrayList<PartialSolution> solutions = new ArrayList<PartialSolution>();
int exam = sol.examine();
if(exam == PartialSolution.accept){
solutions.add(sol);
}
else if(exam != PartialSolution.abandon){
for(PartialSolution p : sol.extend()){
solve(p);
}
}
return solutions;
}
Define a variable in class and add your lists to that list instead of print.
Or make your solve method take a second argument as list and change return type from void to list. For the the first call give it an empty list and pass that list in every recursive call. At the end return that list
If you need the ArrayList just to store the solutions for later use then you should create the list out of the method like:
static ArrayList<PartialSolution> x = new ArrayList<PartialSolution>();
then just add the solution to the list when you print sol.

ArrayList with Objects, find duplicate object fields

Like the title says, I have an ArrayList in java with Objects. The Objects are basically room types in a hotel. they all have a typename, amount of beds etc. Now my problem is, I need to find two objects with the same value for the typename field. I got as far as looping through the list with a for loop but cant figure out how to continue. Here's my code:
import java.util.ArrayList;
public class KamerType {
private String typeNaam;
private int aantalBedden;
private double prijsPerNacht;
public KamerType(String tN, int aB, double pPN){
typeNaam = tN;
aantalBedden = aB;
prijsPerNacht = pPN;
}
public String toString(){
String s = "De kamer van type " + typeNaam + " heeft " + aantalBedden + " bedden en kost " + prijsPerNacht + " euro.";
return s;
}
public static void main(String a[]){
ArrayList<KamerType> kamertypes = new ArrayList<KamerType>();
KamerType k1 = new KamerType("Standaard", 2, 60.0);
kamertypes.add(k1);
KamerType k2 = new KamerType("DeLuxe", 2, 85.0);
kamertypes.add(k2);
KamerType k3 = new KamerType("DeLuxe", 4, 125.0);
kamertypes.add(k3);
KamerType k4 = new KamerType("Hiker", 2, 35.0);
kamertypes.add(k4);
System.out.println("Dit zijn de kamertypen:");
for(KamerType KT : kamertypes){
System.out.println(KT.toString());
}
System.out.println("\nTesten op zelfde typenaam:");
for(KamerType KT : kamertypes){
/*if(kamertypes.get(1).typeNaam == KT.typeNaam){
???
}*/
}
} // end of main
}// end of class
Any help is appreciated :)
Edit: two working answers have been given by npinti and Djorde Ivanovic, npinti gave a more guideline-ish answer. Thanks alot guys :)
Since this sounds like home work, I'll try and provide some basic guidelines as opposed to solutions.
The most basic (albeit less efficient) way would be to do a nest loop and compare the rooms:
(I assume that if an object has all 3 fields which are the same, then, I am referencing the same object. Thus, this part: AND (r1.aantalBedden != r2.aantalBedden)) AND (r1.prijsPerNacht != r2.prijsPerNacht) would help me avoid saying that I have a duplicate (when comparing the same object).
for each KamerType r1 in kamertypes
for each KamerType r2 in kamertypes
if(((r1.typeNaam.equals(r2.typeNaam)) AND (r1.aantalBedden != r2.aantalBedden)) AND (r1.prijsPerNacht != r2.prijsPerNacht))
print("We have duplicates");
An alternative, more standard (if you will) way of doing it would be to:
Make your KamerType class override the equals method, and change it such that two KamerType objects are the same if they have the same typeNaam field.
Use a data structure such as a Set (which does not allow duplicates) and place your KamerType objects in it. This will internally invoke the equals method and if it yields true, then, it will nod add the room to the set, thus allowing you to end up with a list of KamerType objects which are unique.
As a minor note, I also noticed this in your code: if(kamertypes.get(1).typeNaam == KT.typeNaam). In Java, string comparison is done through the equals method, so that should become if(kamertypes.get(1).typeNaam.equals(KT.typeNaam)).
EDIT: As per your comment, the second approach would allow you to solve the problem by going once through the list. There would also be a third approach to solve this problem however I think that it is the worst part of both solutions I provided above (slightly complicated to implement, and the execution might take longer).
Make your KamerType class implement the Comparable interface. This will force you to implement the compareTo()* method. In your implementation, you simply (in this case at least) compare the typeNaam. Then sort the list.
Once that you will have your sorted list, start from the second element of your list and compare it with the previous one. Since your list is sorted by typeNaam, any two objects which have the same typeNaam field will be exactly next to each other in your list, so you would be able to find duplicates without having a O^2 time complexity.
This will allow you to call Collections.sort() and make it sort your collection in a way you want it.
to compare strings, use 'equals' function instead of '=='
like this
if(kamertypes.get(1).typeNaam.equals(KT.typeNaam))
First create a getter method for your field:
public String getTypeNaam() {
return typeNaam;
}
Then you can compare the two values:
if(kt1.getTypeNaam().equals(kt2.getTypeNaam)) {
// Name of kt1 and kt2 is identical
}
If you want to compare each object which each other, use nested loops:
for(KamerType kt1 : kamertypes){
for(KamerType kt2 : kamertypes) {
if(kt1.getTypeNaam().equals(kt2.getTypeNaam)) {
// Name of kt1 and kt2 is identical
}
}
}
Be aware that this also finds two identical objects because they of course have the same name. You can exclude those by adding the condition kt1 != k2 to your if.
Here is what you need. First add getter for your private fields. I used loops with i and j to avoid duplicating.
for(int j = 0;j<kamertypes.size();j++){
for(int i = j+1;i<kamertypes.size();i++){
if(kamertypes.get(j).getTypeNaam().equals(kamertypes.get(i).getTypeNaam()) && i!=j){
System.out.println(kamertypes.get(j) + " and " +kamertypes.get(i));
}
}
}

How do I remove duplicate objects from two separate ArrayLists?

Before beginning, I think that this question has a very simple answer that I'm just overlooking. I figured a few more eyes on the question at hand will be able to point out my problem fairly quickly.
I have two ArrayLists that I want to compare and remove duplicates from each of them. The first ArrayList is an ArrayList of older information where as the second ArrayList contains the new information.
Like so
ArrayList<Person> contactList = new ArrayList();
contactList.add(new Person("Bob");
contactList.add(new Person("Jake");
contactList.add(new Person("Joe");
ontactList.add(new Person("Rob");
ArrayList<Person> updatedContactList = new ArrayList();
updatedContactList.add(new Person("Bob");
updatedContactList.add(new Person("Jake");
updatedContactList.add(new Person("Joe");
updatedContactList.add(new Person("Phil");
My Person class is very simple, created solely for this example
public class Person {
private String name;
public Person(String a_name) {
name = a_name;
}
public String getName() {
return name;
}
}
So, using the above examples, I want to remove all duplicates. I'm trying keep it to just the two ArrayLists if possible, but am willing to do a deep clone of one of the ArrayLists if I have to.
So I want the resulting ArrayList to have the following information in it once the comparison is done
contactList //removed Person
- Rob
updatedContactList //new Person
- Phil
Here is the code I've put together
for(int i = 0; i < contactList.size(); i++) {
for(int j = 0; j < updatedContactList.size(); j++) {
if(contactList.get(i).getName().equals(updatedContactList.get(j).getName())) {
//removed friends
contactList.remove(contactList.get(i));
//new friends ---- only one at a time works
//updatedContactList.remove(updatedContactList.get(j));
}
}
}
I'm only able to remove a Person from one of the ArrayLists in the above loop otherwise I get incorrect results.
So my question is, is there an easy way to remove the duplicated elements from both ArrayLists? If so, how do I go about it.
I realize that I could probably deep clone the updated ArrayList and just remove the objects from that one, but I'm wondering if there is a way without having to clone it.
I also realize that I could just stuff all the elements into a Set and it would remove the duplicates, but I want to keep the 'removed' and 'new' Person objects separate.
What you really have is not lists, but sets: model both the old and the new contacts as a Set. Also implement equals and hashCode for your Person class to ensure proper operation.
Once you have that, you'll be able to write one-liners to calculate the set differences (which is what you need):
final Set<Person> contactsBackup = new HashSet<>(contacts);
contacts.removeAll(updatedContacts);
updatedContacts.removeAll(contactsBackup);
Note that this involves making one more copy, but it is not a deep copy—only references are copied. This is a very leightweight operation and you should not worry about its impact.
If, for some reason not at all obvious to me, you really need lists, the same code will work for them, too (List also defines removeAll), but you will have to live with O(n2) complexity this operation entails for lists.
Override equals() and hashCode() in your Person class and simply do:
Set<Person> temp = new HashSet<>(contactList);
contactList.removeAll(updatedContactList);
updatedContactList.removeAll(temp);
temp.clear(); // not necessary if this code is in a method
Create a Set and addAll from both the ArrayLists.
Set<Person> set = new ArrayList<Person>();
http://docs.oracle.com/javase/6/docs/api/java/util/Set.html
This is a one line elegant solution making use of the Java 8 capabilities
public static final <T> void removeCommonEntries(Collection<T> a, Collection<T> b){
b.removeIf(i -> a.remove(i));
}
I use to put this solution in a custom CollectionUtils.
In this case use Set and not List (this is used if you are getting data from DB using say Hibernate) if possible. Then you can override equals and hashcode method in person class so that while adding required comparisons can be made and duplicates can be taken out. LinkedHashSet can be used as Lists can become slow as data in it grows.

why java.util.Set can't return any value?

java.util.Set specifies only methods that return all records (via Iterator or array).
Why is there no option to return any value from Set?
It has a lot of sense in the real life. For example, I have a bowl of strawberries and I want to take just one of them. I totally don't care which one.
Why I can't do the same in java?
This is not answerable. You'd have to ask the original designers of the Java collections framework.
One plausible reason is that methods with non-deterministic behavior tend to be problematic:
They make unit testing harder.
They make bugs harder to track down.
They are more easily misunderstood and misused by programmers who haven't bothered to read the API documentation.
For hashtable-based set organizations, the behavior a "get some element" method is going to be non-deterministic, or at least difficult to determine / predict.
By the way, you can trivially get some element of a non-empty set as follows:
Object someObject = someSet.iterator().next();
Getting a truly (pseudo-)random element is a bit more tricky / expensive because you can't index the elements of a set. (You need to extract all of the set elements into an array ...)
On revisiting this, I realized that there is another reason. It is simply that Set is based on the mathematical notion of a set, and the elements of a set in mathematics have no order. It is simply meaningless to talk about the first element of a mathematical set.
A java.util.Set is an unordered collection; you can see it as a bag that contains things, but not in any particular order. It would not make sense to have a get(int index) method, because elements in a set don't have an index.
The designers of the standard Java library didn't include a method to get a random element from a Set. If you want to know why, that's something you can only speculate about. Maybe they didn't think it was necessary, or maybe they didn't even think about it.
It's easy to write a method yourself that gets a random element out of a Set.
If you don't care about the index of the elements, try using Queue instead of Set.
Queue q = new ArrayDeque();
q.element(); // retrieves the first object but doesn't remove
q.poll(); // retrieves and removes first object
While a plain Set is in no particular, SortedSet and NavigableSet provide a guaranteed order and methods which support this. You can use first() and last()
SortedSet<E> set = ...
E e1 = set.first(); // a value
E e2 = set.last(); // also a value.
Actually the iterator is a lot better then using get(position) (which is something you can do on a java.util.List). It allows for collection modifications during the iterations for one thing. The reason you don't have them in sets is probably because most of them don't guarantee order of insertion. You can always do something like new ArrayList<?>(mySet).get(position)
If you are not concerned with performance you can create a new type and back the data in an arraylist.
( Please note before donwvoting this is just an naive implementation of the idea and not the proposed final solution )
import ...
public class PickableSet<E> extends AbstractSet<E>{
private final List<E> arrayList = new ArrayList<E>();
private final Set<E> hashSet = new HashSet<E>();
private final Random random = new Random();
public boolean add( E e ) {
return hashSet.add( e ) && arrayList.add( e );
}
public int size() {
return arrayList.size();
}
public Iterator<E> iterator() {
return hashSet.iterator();
}
public E pickOne() {
return arrayList.get( random.nextInt( arrayList.size() ) );
}
}
Of course, since you're using a different interface you'll have to cast to invoke the method:
Set<String> set = new PickableSet<String>();
set.add("one");
set.add("other");
String oneOfThem = ((PickableSet)set).pickOne();
ie
https://gist.github.com/1986763
Well, you can with a little bit of work like this
Set<String> s = new HashSet<String>();
Random r = new Random();
String res = s.toArray(new String[0])[r.nextInt(s.toArray().length)];
This grabs a randomly selected object from the set.

Categories

Resources