ArrayList with Objects, find duplicate object fields - java

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));
}
}
}

Related

How to remove duplicates from an ArrayLlist of ArrayLists that are composed of custom objects

I have a recursive function that generates a list of lists that keeps tracks of valid hand combinations for a card game:
List<List<HandComponent>> validSCompArrangements = new ArrayList<>();
This list is populated by the recursive function successfully but often has duplicate sub-lists (by content but not by not order) that are unavoidable due to the required nature of the function. I wish to remove these duplicate sub-list entries (List<\HandComponent>) so that the above list in the end only features sub-lists that are unique in content, as order does not matter.
Here is the important part of the HandComponent class:
public class HandComponent {
private Type mType;
private Card mCard; // For runs this is the middle card
private Source mSource;
public HandComponent(Type type, Card card, Source source)
{
init(type, card, source);
}
public enum Type {PAIR, TRIPLE, QUAD, RUN}
public enum Source {STOLEN, SECRET, EITHER}
...
}
A sub-list List should only be considered equal to another sub-list if it contains the same exact HandComponents (i.e. the Type, Card, and Source between components of each list must be the same). Card is another enum defined in another file.
So, if two lists in "validSCompArrangements" are
(PAIR,CARD1,STOLEN), (TRIPLE,CARD7,STOLEN), (RUN, CARD8, SECRET)
and
(TRIPLE,CARD7,STOLEN), (RUN, CARD8, SECRET), (PAIR,CARD1, STOLEN)
they should be considered the same since they ultimately contain the same HandComponents even though the order is different and one should be removed so that "validSCompArrangements" only contains that unique list once.
Looking into this I've found bits and pieces on how to solve this problem but nothing that features this combination of a list of lists with custom objects.
One method seems to be to implement a custom Comparator that compares HandComponent instances to use with Collections in order to sort the sub-lists and then another custom Comparator to compare these sorted sub-lists for duplicates, though that seems a tad clunky and I'm not entirely sure how to override the compare method and what kind of return it expects for each comparator I'd need to make. The only other thing I've seen gestured at is that since for my usage the order of both the sub-lists and the main "validSCompArrangements" list itself don't matter, that I should be using Sets and a HashSet to solve this problem instead, I have no idea how to use those to fix this issue, other than that I might need to override the hashCode and equals methods for my HandComponent class, again, not being sure how to do so.
Overall I'm just a bit confused since any example I can manage to find thats remotely related to this usually is talking about just one list of custom objects that contain primatives and not enums, or a list of lists that uses only primatives and no custom objects at all. The fact this is a list of lists of custom objects who's members are enums has me a tad lost on how to go about this.
For example the marked answer in this question: Using collection to remove duplicate Lists, that only handles a portion of my problem, doesn't even seem to work for me despite the OP saying it does. Running that code as is, other than changing
Set<Integer> dedupedCollection = new HashSet<Integer>();
to
Set<List<Integer>> dedupedCollection = new HashSet<>();
as it was clearly meant to be, produces a collection of 3 entries where the second entry of 5, 10, 5 isn't seen as a duplicate and ignored as the OP suggested it should.
EDIT:
So far the closest thing I've found is converting my top-level list to a HashSet using:
Set<List<HandComponent>> handSet = new HashSet<>(validSCompArrangments);
but this only eliminates duplicate lists if their order is the same (which I am guessing is due to the nature of List's default implementation of "equals()"), while I need it to consider lists that are the same in content but different in order as duplicates also. One way around this would be to use Sets for the HandComponent sub-lists as well since they don't care about order innately, but this would prevent those sets from having duplicate HandComponents which I do need to be allowed.
As you said, you just need to implement equals :)
I've provided you how to implement equals method in the HandComponent class and how to use HashSet to getting only the combinations without duplicates.
I've implemented it in Java 8, you can also try to change it using for loop if you want :)
Here is the equals implementation of `HandComponent
public class HandComponent {
public enum Type {PAIR, TRIPLE, QUAD, RUN}
public enum Source {STOLEN, SECRET, EITHER}
public enum Card {ACE, ONE, TWO, TRHEE}
private Type type;
private Card card;
private Source source;
public HandComponent(Type type, Card card, Source source) {
this.type = type;
this.card = card;
this.source = source;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof HandComponent)) {
return false;
}
HandComponent handComponent = (HandComponent) o;
if (type != handComponent.type) {
return false;
}
if (card != handComponent.card) {
return false;
}
if (source != handComponent.source) {
return false;
}
return true;
}
#Override
public String toString() {
return "HandComponent=[" + String.join(", ", Arrays.asList(type.toString(), card.toString(), source.toString())) + "]";
}
}
And below you can see how you can use it
public class Main {
public static void main(String[] args) {
// Creating 2 hand components
HandComponent handComponent1 = new HandComponent(HandComponent.Type.PAIR, HandComponent.Card.ACE, HandComponent.Source.STOLEN);
HandComponent handComponent2 = new HandComponent(HandComponent.Type.QUAD, HandComponent.Card.TRHEE, HandComponent.Source.EITHER);
// 2 combinations with the same card, but different order => they are the same
List<HandComponent> firstCombination = Arrays.asList(handComponent1, handComponent2);
List<HandComponent> secondCombination = Arrays.asList(handComponent2, handComponent1);
// Mixing 2 combinations together
List<List<HandComponent>> combinations = Arrays.asList(firstCombination, secondCombination);
// printing the mix
System.out.println("Before: " + combinations);
// removing duplicates
List<ArrayList<HandComponent>> collect = combinations.stream() // having a stream of list<HandComponent>
.map(HashSet::new) // converting to HashSet, which mean there won't be duplicate in the combinations.
.distinct() // getting only the distinct combinations
.map(ArrayList::new) // reconverting to array list
.collect(Collectors.toList()); // collecting them as list
// result without duplicates
System.out.println("After: " + collect);
// You can now implement it with loop and no java 8 :)
}
}
What ended up working best for me was to implement the "equals()" method for my HandComponent class as suggested by Jiajie Xu, along with the "hashCode()" method automatically generated by Android Studio by using the option in the context menu or Alt + Insert:
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HandComponent that = (HandComponent) o;
return mType == that.mType &&
mCard == that.mCard &&
mSource == that.mSource;
}
#Override
public int hashCode() {
return Objects.hash(mType, mCard, mSource);
}
I then also made the class implement the Comparable interface for use with the Collections class and specified the sort order priority of HandComponent instances within the "compareTo()" method like so:
#Override
public int compareTo(#NonNull HandComponent other) {
// Check Type first
int compareResult = mType.compareTo(other.mType);
if(compareResult == 0)
{
// Check Card second
compareResult = mCard.compareTo(other.mCard);
if(compareResult == 0)
{
// Check Source last
compareResult = mSource.compareTo(other.mSource);
}
}
return compareResult;
}
Since the default implementation of Comparable for List requires list order to be the same in order to return "true" when comparing two lists, I needed to sort my List of Lists every time I wanted to remove duplicates, which was perfectly fine as I benefited from the organization later on.
Ultimately, this allowed me remove the duplicates from my List of Lists of custom objects by first making sure the sub-lists of HandComponent were all sorted and then creating a HashSet of the top-level list.
List<List<HandComponent>> unsortedList = new ArrayList<>();
... // Populate list
for(int i = 0; i < unsortedList.size(); i++)
{
Collections.sort(unsortedList.get(i));
}
Set<List<HandComponent>> sortedDeDupedSet = new HashSet<>(unsortedList);
// Convert back to list since I need order to matter again later on
List<List<HandComponenet>> sortedDeDupedList = new ArrayList<>(sortedDeDupedSet);
This correctly removes duplicates from the top-level list now that I have properly implemented the "equals()" and "hashCode()" methods, as well as sorted the lists before hand with "compareTo()" by leveraging List's default Comparable implementation. Having to use a for loop to sort the lists themselves since I'm restricted to Java 7 does feel a little bad, but like I said before ultimately it was useful to have the lists sorted for other purposes and a lot of time and code is still saved from using a HashSet, versus the nested for loops that would be required to compare each List entry manually.

Return inside a method one or two arrays

Its not duplicated i have read all and nothing suite in my case so please read it and answer it.I have two arrays.One is Vehicle and the other is pin.This is a part of code and it is only the method.
First question :
if i have declare the arrays on the same main out of
this method the way i pass them on the method is right?With other words the parameteres
are good or need (int vehicle[],int pin[]) or something else?
Second question +=
i dont know what it does but i think that in the array pin it takes
as an ecample the pin[1] cost has 10.The number 10 is taken by
getcostosvehicle();(we put it from userinput) so when the array fills
and it hasnt any slot then the costs will be finished.As a result will
have lets say the ended slot is 20 in pin[20] lets say it has 350.The
return statement will give us only the last cost?It would be better to
write return pin[i]; so in that way it will return all the pin with
the whole costs of each one slot,am i right?
Third question
On this code and that i want to write me as an answer could you return
two arrays?I mean return pin[i],vehicle[i]; not only return pin[i];.If
yes,could you do an answer and doesnt need to fill in the vehicle
array.Just to show me if this can happen.
public static int getallcosts(vehicle[],pin[]) {
int costos = 0;
for(int i =0; i < pin.length; i++) {
costos += pin[i].getcostosvehicle();
}
return costos;
}
if i have declare the arrays on the same main out of this method the way i pass them on the method is right?With other words the parameteres are good or need (int vehicle[],int pin[]) or something else?
I'm not sure I understand you correctly but of course getallcosts(vehicle[],pin[]) won't compile, i.e. you need to define the type of the arrays (or the names if vehicle and pin would actually be the types).
It would be better to write return pin[i]; so in that way it will return all the pin with the whole costs of each one slot,am i right?
No, you can only have one return value. If you want to return multiple values then you need to wrap them in an object (array, list, pojo, etc.).
On this code and that i want to write me as an answer could you return two arrays?
See the part above: if you want to return multiple arrays you need to add them so some object and return that object. Since you didn't provide the types for the arrays I'll use another example:
class Result {
String[] strings;
int[] numbers;
}
Result someMethod() {
Result r = new Result();
r.strings = new String[]{"a","b","c"};
r.numbers= new int[]{1,2,3};
return r;
}
First question:
If you are calling a method (so you're not defining it) yuo can write parameters as you do, without type.
Otherwise you need to specify type. In this case you are defining a new method so you need to specify type of parameters.
Second question:
'+=' it's like write
costos = costos + pin[i].getcostosvehicle();
So you will add to the current value of 'costos' the 'costos' of vehicle retrieved by 'getcostosvehicle()';
Third question:
As i know you can't return two Objects of any type in return statement.
So you'll need to reorganize your code to do operation first on an array and return it and then on the other one and return it.
For example you can do a method that have as parameter a generica array do some logic inside and then return it. You will call this method for the first array and then for the second.
Example:
public int[] method(int[] array){
/*do something
*/
return array;
}
Then you will call:
firstArray = method(firstArray);
secondArray = method(secondArray);
If you want more, or i have to change something comment please.

Hash table Java insert

I am new to Java and I am trying to learn about hash tables. I want to insert objects into my hash table and then be able to print all the objects from the hash table at the end. I am not sure I am doing doing this right because I have read that I need to override the get() method or hashCode() method but I am not sure why.
I am passing in String objects of student names. When I run the debugger after my inserts, it shows the key as "null" and the indexes of my inserts are at random places in the hash table. Ex. 1, 6, 10
This is how I have been adding. Can anyone tell me if this is correct and do I actually need to override things?
Thanks in advance!
CODE
Hashtable<String,String> hashTable=new Hashtable<String,String>();
hashTable.put("Donald", "Trump");
hashTable.put("Mike", "Myers");
hashTable.put ("Jimmer", "Markus");
You are doing things correctly. Remember, a Hashtable is not a direct-access structure. You can't "get the third item from a Hashtable", for example. There is no real meaning to the term "index" when you're talking about a Hashtable: numerical indexes of items mean nothing.
A Hashtable guarantees that it will hold key-value pairs for you, in a way that it will be very fast to conclude a value based on a key (for example: given Donald, you will get Trump very quickly). Of course, certain conditions have to be fulfilled for this to work right, but for your simple String-to-String example, that works.
You should read more about hash tables in general, to see how they really work behind the scenes.
EDIT (as per OP's request): you are asking about storing Student instances in your Hashtable. As I mentioned above, certain conditions have to be addressed for a Hashtable to work correctly. Those conditions are concerning the key part, not the value part.
If your Student instance is the value, and a simple String is the key, then there's nothing special for you to do, because the String primitive already answers all of the conditions required for a proper Hashtable key.
If your Student instance is the key, then the following conditions must be met:
Inside Student, you must override the hashCode method in such a way that subsequent invocations of hashCode will return exactly the same value. In other words, the expression x.hashCode() == x.hashCode() must always be true.
Inside Student, you must override the equals method in such a way that it will only return true for two identical instances of Student, and return false otherwise.
These conditions are enough for Student to function as a proper Hashtable key. You can further optimize things by writing a better hashCode implementation (read about it... it's quite long to type in here), but as long as you answer the aforementioned two, you're good to go.
Example:
class Student {
private String name;
private String address;
public int hashCode() {
// Assuming 'name' and 'address' are not null, for simplification here.
return name.hashCode() + address.hashCode();
}
public boolean equals (Object other) {
if (!(other instanceof Student) {
return false;
}
if (other == this) {
return true;
}
Student otherStudent = (Student) other;
return name.equals(otherStudent.name) && address.equals(otherStudent.address);
}
}
Try this code:
Hashtable<String,String> hashTable=new Hashtable<String,String>();
hashTable.put("Donald", "16 years old");
hashTable.put("Mike", "20 years old");
hashTable.put ("Jimmer", "18 years old");
Enumeration studentsNames;
String str;
// Show all students in hash table.
studentsNames = hashTable.keys();
while(studentsNames.hasMoreElements()) {
str = (String) studentsNames.nextElement();
txt.append("\n"+str + ": " + hashTable.get(str));
}

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

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

How to make compareTo sort a list alphabetically?

How do I change my code so that it lists the elements in alphabetical order from a to z. Right now it's ordering from z to a. I can't figure it out and am stuck :-/
String sName1 = ((Address)o).getSurname().toLowerCase();
String sName2 = (this.surname).toLowerCase();
int result = (sName1).compareTo(sName2);
return result;
Thanks :)
Just swap the objects you're comparing.
int result = (sName2).compareTo(sName1);
Usually, for ascending sorting you'd like the lefthand side be part of this object and the righthand side be part of the other object. For descending sorting you just swap the both sides (which you thus initially did).
To make your code more intuitive, I'd however rather swap the assignments of sName1 and sName2:
String sName1 = (this.surname).toLowerCase();
String sName2 = ((Address)o).getSurname().toLowerCase();
int result = (sName1).compareTo(sName2);
And get rid of the hungarian notation and unnecessary parentheses as well. The following sums it:
public class Address implements Comparable<Address> {
private String surname;
// ...
public int compareTo(Address other) {
return this.surname.toLowerCase().compareTo(other.surname.toLowerCase());
}
}
You may want to add nullchecks if you can't guarantee that surname is never null.
See also:
Object Ordering tutorial at Sun.com
Different ways to sort a list of Javabeans

Categories

Resources