Updating the objects of a Set in Java - java

I am trying to read from a file and count how many times each string appears in the file. I am using a HashSet on the Object Item which i have created as follows :
Now in my main i am trying to read the file and add each String in the file to my set. Also while adding i am trying to increment the count of an item in the set which is appearing more than once. Here's my implementation for that :
package pack;
public class Item {
public String name;
public int count=1;
public Item(String name)
{
this.name = name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + count;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Item other = (Item) obj;
if (count != other.count)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
For an input file like this :
chair table teapot
teapot book table
chair floor ceiling
wall chair floor
it is giving an output as follows :
wall appears 1 times
book appears 1 times
table appears 2 times
floor appears 2 times
teapot appears 2 times
chair appears 1 times
ceiling appears 1 times
chair appears 2 times
Here the set is having duplicate elements which i don't want. What is the correct way to update the objects inside a set?

i think this'll help you.
Create list of all keywords, and use code below.
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("a");
// get all Unique keywords
Set<String> set = new HashSet<String>(list);
for(String keyword: set){
System.out.println(keyword + ": " + Collections.frequency(list, keyword));
}
}
output
b: appears 1 time
a: appears 2 time

Your Item class uses the count field in its definition of equals and hashCode. This means that when you call set.contains(i) for the second occurrence of the string, contains will return true since count==1. You then increment count, and when you call set.contains(i) for the third occurrence of the string contains will return false, since the count of the Item in the set does not match the count of the Item you are passing to contains.
To fix this, you should change your definition of equals and hashCode to consider only the string and not the count.
This implementation will work, but is overly complex. You could simply create a Map<String, Integer> and increase the Integer (count) each time you see a new occurrence of the string.

Your Implementation is right. But your Item class equals method has only problem.
In equals method you have used count variable also. But name is only the unique field in that class. You have used count+name as unique. So it will create problem.

HashSet uses hashCode and equals to determine identity, so you should change hashCode and equals to work with the name only when you don't want to include the count of items in the test for equality:
package pack;
public class Item {
public String name;
public int count=1;
public Item(String name)
{
this.name = name;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Item other = (Item) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}

I think the problem is in your equals method, when you do this check:
if (count != other.count)
return false;

Have you considered using a HashMap for your problem: put the name in the key and the counter in the value. This way you don't need an Item class at all.

Related

Is there any way to find the duplicate values i.e ArrayList from a hash map?

For eg:-
I have declared a hashMap in the form of:
Map<String, List<Tracks>> dupItems = new LinkedHashMap();
Tracks is a model class that contains name, address, and age.
and I added items in a Tracks
and I added Items as:-
dupItems.add("Project",tracks);
dupItems.add("Report",tracks);
and what I want is a list of duplicate tracks i.e how can I match the items on the basis of values of tracks i.e all values have to be similar. same name, same address, and same age.
If you are looking for the duplicate map values, that is, duplicate lists of Tracks:
One way you can do this is iterate over the values, putting them all in a Set data structure. If you find that the value was already in the set, then it's a duplicate, and you add it to a separate data structure that keeps a record of the duplicate values:
Set<List<Tracks>> findDuplicateValues(Map<String, List<Tracks>> dupItems) {
Set<List<Tracks>> allValues = new HashSet<>();
Set<List<Tracks>> duplicateValues = new HashSet<>();
for (List<Tracks> value : dupItems.values()) {
if (!allValues.add(value)) {
// It's a duplicate!
duplicateValues.add(value);
}
}
return duplicateValues;
}
For this to work reliably, the Tracks class has to implement the equals and hashCode methods. Comparing two objects with the same values using the equals method should return true.
On the other hand, if you are looking for the duplicate Tracks values, no matter which map value list contains them:
you just need to add a loop to the previous method:
Set<List<Tracks>> findDuplicateValues(Map<String, List<Tracks>> dupItems) {
Set<List<Tracks>> allValues = new HashSet<>();
Set<List<Tracks>> duplicateValues = new HashSet<>();
for (List<Tracks> value : dupItems.values()) {
for (Tracks value : values) {
if (!allValues.add(value)) {
// It's a duplicate!
duplicateValues.add(value);
}
}
}
return duplicateValues;
}
I'm assuming we search for duplicate Tracks instances.
public Set<Tracks> findDuplicates(Map<String, List<Tracks>> dupItems) {
Set<Tracks> all = new HashSet<>();
return dupItems.values().stream()
.flatMap(list -> list.stream()) // build a single list of Tracks
.filter(t -> !all.add(t)) // add track to all set but only continue if duplicate
.collect(Collectors.toSet()); // store the remaining tracks in a set (to avoid duplicates in the result
}
A Set will check on duplicates only if the equals() function is correctly implemented.
My eclipse editor generated :
private static class Tracks {
String name;
String address;
int age;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((address == null) ? 0 : address.hashCode());
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tracks other = (Tracks) obj;
if (address == null) {
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
You can also have a look at Apache Commons EqualsBuilder

Example of 2 objects in java having same hash value but equals method returns false on them

We all know that if equals method returns true, then two objects are equal.
Can anybody give an example where 2 objects have the same hash value but they are actually different?
I'm assuming you're familiar with the contract(s) associated with overriding equals() and hashCode(), and the implications of a collision-prone hashCode implementation. Given that, the following trivial example uses an object that holds two Integers and implements a very simple hashCode, and demonstrates how easy it is to have two objects that aren't equal but have the same hashCode. Providing a more sophisticated hashCode algorithm can alleviate this.
The output of running main is:
hashCodes: ih1: 6, ih2: 6
equals: false
Example code:
package example.stackoverflow;
public class IntHolder
{
private Integer primaryData;
private Integer secondaryData;
public IntHolder(Integer primaryData, Integer secondaryData)
{
this.primaryData = primaryData;
this.secondaryData = secondaryData;
}
#Override
public int hashCode()
{
return ((primaryData == null) ? 0 : primaryData.hashCode()) +
((secondaryData == null) ? 0 : secondaryData.hashCode());
}
#Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IntHolder other = (IntHolder) obj;
if (primaryData == null)
{
if (other.primaryData != null)
return false;
}
else if (!primaryData.equals(other.primaryData))
return false;
if (secondaryData == null)
{
if (other.secondaryData != null)
return false;
}
else if (!secondaryData.equals(other.secondaryData))
return false;
return true;
}
public static void main(String[] args)
{
IntHolder ih1 = new IntHolder(1, 5);
IntHolder ih2 = new IntHolder(3, 3);
System.out.println("hashCodes: ih1: " + ih1.hashCode() + ", ih2: " + ih2.hashCode());
System.out.println("equals: " + ih1.equals(ih2));
}
}
For reference, Eclipse's auto-generated hashCode() for the IntHolder class is:
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result
+ ((primaryData == null) ? 0 : primaryData.hashCode());
result = prime * result
+ ((secondaryData == null) ? 0 : secondaryData.hashCode());
return result;
}
String str1="abcdef";
String str2="abcdfG";
They both have the same hashcode and equals method returns false.
public class Employee {
protected long employeeId;
public boolean equals(Object o){
if(o == null) return false;
if(!(o instanceof) Employee) return false;
Employee other = (Employee) o;
return this.employeeId == other.employeeId;
}
public int hashCode(){
return (int) this.employeeId;
}
}
In this example, we have overridden the equals method - two employees are equal when they will have same employee id.
If two Employee objects are equal, they will also have the same hash code.
Your Ans -
In this example, we also implemented the hash code - hashcode is the employeeId that is rounded down to an int. That means that many employee id's could result in the same hash code, but these Employee objects would still not be equal, since they don't have the same employee id.

Override indexOf in LinkedList to check for identity, not equality

I have a List (actually a LinkedList) and I add Items to it which implement the equals-method.
The problem is that I add items that are equal, but not identical (like two initialized objects). Now, when I want to get the index of the item I added second, I get, of course, the element of the first item, because indexOf searches for equality and not identity.
I tried to create my own subclass of LinkedList and overwrite the indexOf-method, but this is not possible, because I don't have access to neither the subclass Node nor the Node-Element first.
Here is an example:
public class ExampleObject {
int number;
public ExampleObject(){
number = 0;
}
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
ExampleObject other = (ExampleObject) obj;
if (number != other.number) return false;
return true;
}
public static void main(String[] args) {
LinkedList<ExampleObject> list = new LinkedList<ExampleObject>();
ExampleObject one = new ExampleObject();
ExampleObject two = new ExampleObject();
list.add(one);
list.add(two);
System.out.println(list.indexOf(one)); // '0' as expected
System.out.println(list.indexOf(two)); // '0', but I want to get '1'
}
}
My intention: I need a list of objects, where I want to store initialized objects and edit them later.
Do the iteration yourself, indexOf is just a helper method:
static int indexOfById(List<?> list, Object searchedObject) {
int i = 0;
for (Object o : list) {
if (o == searchedObject) return i;
i++;
}
return -1;
}
There are several solutions to this problem.
1) The correct solution: If you need identity comparison, then you should not override the equals method. You told us that you must override it, as you need it in another place. That indicates a design problem in your software, and you really should solve this.
2) Have a look at the class sun.awt.util.IdentityLinkedList. This is nearly a "normal" LinkedList with the identity behavior for the indexOf method. If you do not want to rely on a class in a sun sub-package, you can copy the code into a class that resides in your packages.
3) You could follow a procedural solution with iterating the list yourself:
public static <E> int indexOf(List<E> list, E searchItem) {
int index = 0;
for (E item : list) {
if (item == searchItem)
return index;
index += 1;
}
return -1;
}
4) Write a wrapper for your objects.
public class IdentityWrapper {
public Object item;
#Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
IdentityWrapper other = (IdentityWrapper) obj;
return item == other.item;
}
}
Then use this wrapper in your list: LinkedList<IdentityWrapper>. Note, that I provided a public item field in the wrapper. Normally, I would do it with a constructor and a private field only.

Filter unique objects from an ArrayList based on property value of the contained object

How will I filter unique object from an arraylist.
List<LabelValue> uniqueCityListBasedState = new ArrayList<LabelValue>();
for (LabelValue city : cityListBasedState) {
if (!uniqueCityListBasedState.contains(city)) {
uniqueCityListBasedState.add(city);
}
}
This is my code. But the problem is that I need to filter not on the object but on the value of a property inside that object. In this case, I need to exclude the objects that has the name.
That is city.getName()
List<LabelValue> uniqueCityListBasedState = new ArrayList<LabelValue>();
uniqueCityListBasedState.add(cityListBasedState.get(0));
for (LabelValue city : cityListBasedState) {
boolean flag = false;
for (LabelValue cityUnique : uniqueCityListBasedState) {
if (cityUnique.getName().equals(city.getName())) {
flag = true;
}
}
if(!flag)
uniqueCityListBasedState.add(city);
}
Assuming you can change the list to set.
Use the Set Collection instead.
A Set is a Collection that cannot contain duplicate elements.
Overwrite the equals() and hashCode() methods of LabelValue (hashCode is not a must in this case):
String name;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
LabelValueother = (LabelValue) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
Here is one way to solve it.
You should override the equals() method and hashCode() of LabelValue.
And the equals() method should use the name property and so should the hashCode() method.
Then your code will work.
PS. Im assuming that your LabelValue objects can be distinguished with just the name property, which is what you seem to need anyway based on your question.

Getting unique enteries from the file

I am parsing a file with more than 4M lines in it. It is of the form a^b^c^d^...^....
Now i want all the unique points(only the 1st two entries should be unique) from the file. So what I do is,
String str;
Set<String> lines = new LinkedHashSet<String>();
Set<String> set = Collections.synchronizedSet(lines);
String str1[] = str.split("\\^");
set.add(str1[0]+"^"+str1[1]);
So this gives me the unique 1st and 2nd unique points from the file. However, I also want the 3rd point(timestamp) i.e str1[2] associated with the above points. The new file should be of the form.
str1[0]^str1[1]^str1[2]
How do I go about doing this?
There are a few solutions that come to mind.
Make a class for the 3 entries.
Override the equals method and only check on the first 2 entries there, so 2 objects are equal if the first 2 entries are equal. Now add all the items to the set.
So what you 'll get in your set is a list with unique first and second points and the first occaurance of your timestamp.
Another solution is to keep two lists, one with your 2 points + time stamp, one with only your 2 points.
The you can do set.contains(...) to check if you already saw the point and if you didn't add to the list with 2 points + timestamp.
Create a class containing the information you need which you will store in the set, but only care about the first two in equals/hashCode. Then you can do:
Set<Point> set = new HashSet<Point>();
String str1[] = str.split("\\^");
set.add(new Point(str1[0], str1[1], str1[2]));
Using:
public class Point {
String str1;
String str2;
String str3;
public Point(String str1, String str2, String str3) {
this.str1 = str1;
this.str2 = str2;
this.str3 = str3;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((str1 == null) ? 0 : str1.hashCode());
result = prime * result + ((str2 == null) ? 0 : str2.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Point other = (Point) obj;
if (str1 == null) {
if (other.str1 != null)
return false;
} else if (!str1.equals(other.str1))
return false;
if (str2 == null) {
if (other.str2 != null)
return false;
} else if (!str2.equals(other.str2))
return false;
return true;
}
}

Categories