Avoiding adding duplicate objects to an ArrayList - java

Here's the gist, essentially I have this method right with the exception of one conditional that I just can't seem to get. The condition is that if addGame() is called twice with the same two Strings it will not store the Game object in the games ArrayList since it will return false. I have already attempted to utilize the ArrayList contains() method to fix it, but the JUnit test I created fails each time. Here's the code for the method:
public class Conference {
private ArrayList<Team> teams;
private ArrayList<Player> players;
private ArrayList<Game> games;
public Conference(){
teams = new ArrayList<Team>();
players = new ArrayList<Player>();
games = new ArrayList<Game>();
}
public boolean addGame(String team1, String team2) {
Game tempgame = new Game(team1, team2, 0, 0);
Team first = new Team(team1, 0, 0, 0);
Team second = new Team(team2, 0, 0, 0);
if(!tempgame.getFirst().equals(tempgame.getSecond())){
games.add(tempgame);
first.addGamesPlayed();
second.addGamesPlayed();
teams.add(first);
teams.add(second);
return true;
}
return false;
}
The Game class is as follows:
package conference;
import java.util.ArrayList;
public class Game {
private String firstTeam;
private String secondTeam;
private int firstTeamGoals;
private int secondTeamGoals;
private ArrayList<Team> team;
public Game(String first, String second, int goals1, int goals2){
this.firstTeam = first;
this.secondTeam = second;
this.firstTeamGoals = goals1;
this.secondTeamGoals = goals2;
team = new ArrayList<Team>();
}
public String getFirst(){
return new String(firstTeam);
}
public String getSecond(){
return new String(secondTeam);
}
public int getFirstTeamGoals(){
return this.firstTeamGoals;
}
public int addFirstTeamGoals(){
return firstTeamGoals++;
}
public int getSecondTeamGoals(){
return this.secondTeamGoals;
}
public int addSecondTeamGoals(){
return secondTeamGoals++;
}
public boolean hasMatchup(String t1, String t2){
if(this.firstTeam.equals(t1) && this.secondTeam.equals(t2)){
return true;
}
if(this.firstTeam.equals(t2) && this.secondTeam.equals(t1)){
return true;
}
return false;
}
}
And the Team class:
package conference;
public class Team {
private String teamName;
private int goalsScored;
private int gamesPlayed;
private int gamesWon;
public Team(String name, int totalGoals, int games, int wins){
this.teamName = name;
this.goalsScored = totalGoals;
this.gamesPlayed = games;
this.gamesWon = wins;
}
public String getName(){
return new String(teamName);
}
public int getTotalGoals(){
return goalsScored;
}
public int addGoals(){
return goalsScored++;
}
public int addGamesPlayed(){
return this.gamesPlayed++;
}
public int getGamesPlayed(){
return gamesPlayed;
}
public int addGamesWon(){
return gamesWon++;
}
public int getGamesWon(){
return gamesWon;
}
public boolean equals(Object obj) {
if (obj == null) {
return false;
} else if (obj == this) {
return true;
} else {
if (!(obj instanceof Team)) {
return false;
} else {
Team temp = (Team) obj;
return this.teamName.equals(temp.getName());
}
}
}
}

Your method is likely failing because though the strings aren't equal, that doesn't mean that the teams or games list don't already have those strings. You need to loop over the games list and check if the name equals team1 or team2, if so, return false.
It's good that you compare the given teams aren't the same, and you can use your parameters to compare, but that's not the only condition you need.
if(!team1.equals(team2)) {
Also, the fact you have new String on values that are already strings makes a new object in memory, (therefore it's pointless) but the equals method should still work.

"The strings are stored in a game object, that's why I can't have two of the same objects stored in the ArrayList ever" (Your comment.)
It seems that in exchange for more memory you could use a Set (HashSet or TreeSet) to store records of previous games played. If you're protecting against identical pairings rather than identical team names (at any point), sort the two String team names consistently and concatenate them into a key. Check whether that key already exists in the set.
EDIT: See the other solution for ArrayList only.

Related

List of Objects to be Unique [duplicate]

This question already has answers here:
How do define my own element class for use with Set
(2 answers)
Why should a Java class implement comparable?
(10 answers)
Closed 6 years ago.
I have a simple class called Stock the code is listed below, and I my requirement is to create a Collection of Stock where the combination of the fields StockId, Code and name should be unique, I am doing this by implementing my own list class. I was wondering if there is any better way to do this
public class Stock {
private Integer stockId;
private String stockCode;
private String stockName;
public Stock() {
}
public Stock(Integer stockId,String stockCode, String stockName) {
this.stockCode = stockCode;
this.stockName = stockName;
}
public Integer getStockId() {
return this.stockId;
}
public void setStockId(Integer stockId) {
this.stockId = stockId;
}
public String getStockCode() {
return this.stockCode;
}
public void setStockCode(String stockCode) {
this.stockCode = stockCode;
}
public String getStockName() {
return this.stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
}
List class
public class StockList {
private List<Stock> listStock;
public StockList(){
listStock = new ArrayList<Stock>();
}
public void add(Stock stock){
boolean result=true;
for(Stock st:listStock){
int count=0;
if(st.getStockId()==stock.getStockId()){
count++;
}
if(st.getStockCode()==stock.getStockCode()){
count++;
}
if(st.getStockName()==stock.getStockName()){
count++;
}
if(count>=3){
result=false;
break;
}
}
if(result) {
listStock.add(stock);
}
}
public List<Stock> getList(){
return listStock;
}
}
I have even tried the Hashset per instructions but it still let me add two Stock objects with same values in every field
import java.util.HashSet;
import java.util.Set;
public class Stock {
private Integer stockId;
private String stockCode;
private String stockName;
public Stock() {
}
public Stock(Integer stockId,String stockCode, String stockName) {
this.stockCode = stockCode;
this.stockName = stockName;
}
public Integer getStockId() {
return this.stockId;
}
public void setStockId(Integer stockId) {
this.stockId = stockId;
}
public String getStockCode() {
return this.stockCode;
}
public void setStockCode(String stockCode) {
this.stockCode = stockCode;
}
public String getStockName() {
return this.stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + stockId+stockCode.hashCode()+stockName.hashCode();
return result;
}
public boolean equals(Object obj) {
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Stock other = (Stock) obj;
int count=0;
if (stockId == other.stockId){
count++;
}
if(stockCode.equalsIgnoreCase(other.stockCode)){
count++;
}
if(stockName.equalsIgnoreCase(other.stockName)){
count++;
}
if(count<3) {
return true;
}
return false;
}
}
You will need to add your Stock objects in a HashSet<Stock>.
Before adding a Stock object to the set, you will be able to check whether the HashSet already contains it by invoking myStockHashSet.contains( stock ). (But even if you go ahead and add a duplicate stock object to the HashSet, the new object will not replace the old object, so there will never be duplicates.)
In order for HashSet to work, it has to be able to tell whether two Stock objects are identical. For this, your Stock class will need to implement hashCode() and equals().
hashCode() will need to hash together the fields stockId, code and name. Recent versions of java offer an Objects.hashCode( Object ... ) convenience method for quickly hashing together your fields. If you are not programming against a recent version of java, you will need to write your own implementation of a hashCode() calculation. Look here for some good advice: Best implementation for hashCode method
equals() should return true only if all these fields are equal in both objects.
NOTE:
do not waste your time with a List, since lists allow duplicates.
do not waste your time implementing Comparable, since this is for ordering objects, not for comparing objects for equality, and HashSet does not care whether your objects implement Comparable.

Order arraylist based on multiple connection

This is my VO
public class SomeVO {
private String name;
private String usageCount;
private String numberofReturns;
private String trendNumber;
private String nonTrendNumber;
private String trendType;
private String auditType;
public SomeVO(String name,String usageCount,String numberofReturns,String trendNumber,String nonTrendNumber,String trendType,String auditType){
this.name = name;
this.usageCount = usageCount;
this.numberofReturns = numberofReturns;
this.trendNumber = trendNumber;
this.nonTrendNumber = nonTrendNumber;
this.trendType = trendType;
this.auditType = auditType;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUsageCount() {
return usageCount;
}
public void setUsageCount(String usageCount) {
this.usageCount = usageCount;
}
public String getNumberofReturns() {
return numberofReturns;
}
public void setNumberofReturns(String numberofReturns) {
this.numberofReturns = numberofReturns;
}
public String getTrendNumber() {
return trendNumber;
}
public void setTrendNumber(String trendNumber) {
this.trendNumber = trendNumber;
}
public String getNonTrendNumber() {
return nonTrendNumber;
}
public void setNonTrendNumber(String nonTrendNumber) {
this.nonTrendNumber = nonTrendNumber;
}
public String getTrendType() {
return trendType;
}
public void setTrendType(String trendType) {
this.trendType = trendType;
}
public String getAuditType() {
return auditType;
}
public void setAuditType(String auditType) {
this.auditType = auditType;
}
}
Here is my values
List<SomeVO> myList = new ArrayList<SomeVO>();
SomeVO some = new SomeVO("A","0","0","123","123","Trend","AuditX");
myList.add(some);
some = new SomeVO("B","1","1","234","234","Non trend","AuditX");
myList.add(some);
some = new SomeVO("C","0","2","345","345","Trend","AuditX");
myList.add(some);
some = new SomeVO("D","2","3","546","546","Trend","AuditX");
myList.add(some);
some = new SomeVO("E","2","4","678","678","Non trend","AuditX");
myList.add(some);
some = new SomeVO("F","0","0","123","123","Non trend","AuditA");
myList.add(some);
some = new SomeVO("G","0","0","123","123","Trend","AuditB");
myList.add(some);
Here is my comparator
public String currentAudit = "AuditX";
public class AuditComparator implements Comparator<SomeVO> {
#Override
public int compare(SomeVO o1, SomeVO o2) {
if(currentAudit.equalsIgnoreCase(o1.getAuditType()) && currentAudit.equalsIgnoreCase(o2.getAuditType())) {
int value1 = o2.getUsageCount().compareTo(o1.getUsageCount());
if (value1 == 0) {
int value2 = o1.getNumberofReturns().compareTo(o2.getNumberofReturns());
if(o1.getTrendType().equalsIgnoreCase("Trend") && o2.getTrendType().equalsIgnoreCase("Trend")) {
if (value2 == 0) {
return o1.getTrendNumber().compareTo(o2.getTrendNumber());
} else {
return value2;
}
} else {
if (value2 == 0) {
return o1.getNonTrendNumber().compareTo(o2.getNonTrendNumber());
} else {
return value2;
}
}
}
return value1;
} else {
return 1;
}
}
}
I am trying to sort the VO based on below conditions
First only set of values of currentAudit should be taken in to
consideration i.e., AuditX
a) then it should be sorted with
Usage count in descending order
b) if same usage count found then it
should be sorted with Return count in ascending order
c) if same
return count then it should check for trendType, if trendType
="Trend" then it should sort with Trend number otherwise nonTrend number.
then it should consider rest all auditType's and sorted with
a),b),c) condition as like currentAudit. I tried achieving it and i
ended up with only above comparator. Expected result: D, A, C, E,
F, G. But i get G,F,D,E,B,A,C. Please help me to update the
comparator above.
Your comparator does not meet a simple condition: it is not stateless. A following should always be true: A>B => B<A. In your case, in some scenarios A>B and B>A.
I resolved it by splitting the actual list in to 2 list based on AuditX and rest in another list. Then used below comparator one by one, and then merged in to a result list. Works good.
for(SomeVO some:myList) {
if(some.getAuditType().equalsIgnoreCase("AuditX")) {
auditX.add(some);
} else {
auditY.add(some);
}
}
Collections.sort(auditX, new AuditComparator());
Collections.sort(auditY, new AuditComparator());
public class AuditComparator implements Comparator<SomeVO> {
#Override
public int compare(SomeVO o1, SomeVO o2) {
int value1 = o2.getUsageCount().compareTo(o1.getUsageCount());
if (value1 == 0) {
int value2 = o1.getNumberofReturns().compareTo(o2.getNumberofReturns());
if (value2 == 0) {
return (o1.getTrendType().equalsIgnoreCase("Trend") && o2.getTrendType().equalsIgnoreCase("Trend")) ?
o1.getTrendNumber().compareTo(o2.getTrendNumber()):o1.getNonTrendNumber().compareTo(o2.getNonTrendNumber());
} else {
return value2;
}
}
return value1;
}
The return 1 at the bottom of the comparator makes a bug.
The comparator shall only return 1 if the second element is bigger than the first one, but if they're different, you always return 1, so the very first sorting criteria will be messy.
// a helper for case insensitive comparison
private int compareIgnoreCase(String o1,String o2) {
return o1.toLowercase.compareTo(o2.toLowercase());
}
#Override
public int compare(SomeVO o1, SomeVO o2) {
int result=compareIgnoreCase(o1.getAuditType(),o2.getAuditType());
if (result==0) {
// we need to go to the 2nd criteria
result=o2.getUsageCount().compareTo(o1.getUsageCount());
}
if (result==0) {
// ok, 1st and 2nd criteria was the same, go to the 3rd
result=o1.getNumberofReturns().compareTo(o2.getNumberofReturns());
}
if (result==0) {
// check trends
...
}
return result;
}
I found that this representation of multiple comparison criteria makes the code much easier to follow. We first do the highest priority of comparison, and go on with further comparions if the previous comparisons returned that the two elements are the same (i.e. result is still zero).
In case you need to make a descending sorting at some level, simply put a -, e.g.:
result=-o1.something.compareTo(o2.something)
It is a good idea to have only one exit point in a method (this also makes easier to follow what is happening).

In Collections, how can i get the index using the indexOf method in the following example

class Fruit{
public String name;
Fruit(String name){
this.name = name;
}
}//end of Fruit
class FruitList{
public static void main(String [] arg5){
List<Fruit> myFruitList = new ArrayList<Fruit>();
Fruit banana = new Fruit("Banana");
//I know how to get the index of this banana
System.out.println("banana's index "+myFruitList.indexOf(banana));
//But i'm not sure how can i get the indices for the following objects
myFruitList.add(new Fruit("peach"));
myFruitList.add(new Fruit("orange"));
myFruitList.add(new Fruit("grapes"));
}//end of main
}//end of FruitList
Since the rest of the objects that i've added to the ArrayList have no references, i'm not quite sure how their index can be retrieved. Please help, Thanks so much.
It does not matter which reference the object has if you redefine the equals and hashcode methods in the Fruit class. indexOf, contains, etc use the equals(...) method to decide if the object exists inside the collection.
For example, your Fruit class, could be like this (I changed your public String name to private):
public class Fruit {
private String name;
public Fruit(String name){
this.name = name;
}
public String getName() {
return name;
}
#Override
public int hashCode() {
int hash = 7;
hash = 89 * hash + Objects.hashCode(this.name);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Fruit other = (Fruit) obj;
if (!Objects.equals(this.name, other.name)) {
return false;
}
return true;
}
Then:
Fruit f = new Fruit("orange");
myFruitList.indexOf(f); // this should return the orange fruit index (would be 1 in your example).

sorting collection wrt primitive value

suppose, I have a student class with roll number and name. I want to sort it out wrt roll number. I tried the following .Here is my code:
package CollectionDemo;
import java.util.*;
class student1 implements Comparable<student1>{
int rollNo;
String name;
student1(int rollNo,String name){
this.rollNo=rollNo;
this.name=name;
}
#Override
public boolean equals(Object o){
if((o instanceof student1) && (((student1)o).rollNo == rollNo)){
return true;
}
else
{
return false;
}
}
#Override
public int hashCode(){
return 1;
}
public int compareTo(student1 s) {
return s.rollNo;
}
public String toString(){
return "["+rollNo+","+name+"]";
}
}
public class treeSetDemo {
public static void main(String... a){
Set<student1> set=new TreeSet<student1>();
set.add(new student1(102,"Anu"));
set.add(new student1(101,"Tanu"));
set.add(new student1(103,"Minu"));
System.out.println("elements:"+set);
}
}
o/p: elements:[[102,Anu], [101,Tanu], [103,Minu]]
so, its not sorting:( how to make it correct .
thanks for your help.
================================================
thanks for all your help. The following code runs fine, but now I want to know how it works, if i comment out equals and hashcode method.
package CollectionDemo;
import java.util.*;
class student1 implements Comparable<student1>{
int rollNo;
String name;
student1(int rollNo,String name){
this.rollNo=rollNo;
this.name=name;
}
/* #Override
public boolean equals(Object o){
if((o instanceof student1) && (((student1)o).rollNo == rollNo)){
return true;
}
else
{
return false;
}
}
#Override
public int hashCode(){
return 1;
}
*/
public int compareTo(student1 s) {
System.out.println("hello:"+(this.rollNo-s.rollNo));
return this.rollNo-s.rollNo;
}
public String toString(){
return "["+rollNo+","+name+"]";
}
}
public class treeSetDemo {
public static void main(String... a){
Set<student1> set=new TreeSet<student1>();
set.add(new student1(102,"Anu"));
set.add(new student1(101,"Tanu"));
set.add(new student1(103,"Minu"));
System.out.println("elements:"+set);
}
}
OP:
run:
hello:-1
hello:1
elements:[[101,Tanu], [102,Anu], [103,Minu]]
BUILD SUCCESSFUL (total time: 0 seconds)
you have to change compareTo method in bellow way
public int compareTo(student1 s) {
if(s.rollNo == this.rollNo){
return 0;
}else if(s.rollNo > this.rollNo){
return -1;
}else{
return 1;
}
}
- If you want to sort on the basis of only one attribute, then go with java.lang.Comparable<T> Intereface, along with Collections.sort(List l).
- But if you aim is to sort it on the basis of more then one attribute then go for java.util.Comparator<T> along with Collections.sort(List l, Comparator c).
Eg:
import java.util.Comparator;
public class Fruit implements Comparable<Fruit>{
private String fruitName;
private String fruitDesc;
private int quantity;
public Fruit(String fruitName, String fruitDesc, int quantity) {
super();
this.fruitName = fruitName;
this.fruitDesc = fruitDesc;
this.quantity = quantity;
}
public String getFruitName() {
return fruitName;
}
public void setFruitName(String fruitName) {
this.fruitName = fruitName;
}
public String getFruitDesc() {
return fruitDesc;
}
public void setFruitDesc(String fruitDesc) {
this.fruitDesc = fruitDesc;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public int compareTo(Fruit compareFruit) {
int compareQuantity = ((Fruit) compareFruit).getQuantity();
//ascending order
return this.quantity - compareQuantity;
//descending order
//return compareQuantity - this.quantity;
}
public static Comparator<Fruit> FruitNameComparator
= new Comparator<Fruit>() {
public int compare(Fruit fruit1, Fruit fruit2) {
String fruitName1 = fruit1.getFruitName().toUpperCase();
String fruitName2 = fruit2.getFruitName().toUpperCase();
//ascending order
return fruitName1.compareTo(fruitName2);
//descending order
//return fruitName2.compareTo(fruitName1);
}
};
}
I think this implementation is close to recommended:
#Override
public int compareTo(Object other) {
if(other == null || !(other instanceOf student)){
throw new IllegalArgumentException();
}
student s = (student) other;
if(this.rollNo > s.rollNo){
return 1;
} else if (this.rollNo < s.rollNo){
return -1;
} else {
return 0;
}
}
If you are using Comparable interface then your compareTo() method should return the comparison not equals method , Google comparable example.
Check this link
In your compareTo method, you are just returning the value of the object you are comparing to. You need to return the difference, of the attribute of the invoking instance and passed instance.
So, change your compareTo method to the below one: -
#Override
public int compareTo(student1 s) {
return this.rollNo - s.rollNo;
}
NOTE: - Only sign is important for Collections.sort, so you don't really need an if-else block to return -1, 0, or 1. Just return the difference. That's all.
P.S : -
Your hashcode implementation is a very poor one. It will put every instances in the same bucket.
#Override
public int hashCode(){
return 1; // All the instances will have the same hashcode.
}
Ideally, you should use only those attributes to calculate the hashCode which you have used to compare your two instances, here its rollNo.
So, rather than returning simply a value 1, you can have some equations, that calculates your hashcode, taking into to consideration your rollNo and a large prime number also.
You can go through Effective Java - Item#9 for more explanation of this topic.
Now, that your code is working fine, lets move to your 2nd doubt.
equals and hashCode methods are not used when you want to compare two objects that will be used while sorting. We override equals and hashCode methods in order to check whether an instance is equal to another instance later on.
So, compareTo method is not concerned with whether you have ocerrided equals ad hashCode method or not. And you can also infer from name as to what the two methods does, and can they be related or not.
Moreover, equals method is defined in Object class, whereas compareTo method is declared in Comparable interface. So, they are not interrelated.
Check the documentation of these methods: - Object#equals, Object#hashCode, and Comparable#compareTo

.equals() method to detect duplicate array elements (tried #Override)

I have a simple loop that checks for any duplicate results,
where studresults holds my results , result is the object result given to the method and r is the current object from the array.
I have been using this method successfully throughout the program although it is not working in this case even though when I debug result and r , are exactly the same does anyone know why this might be? I have tried #Override already as suggested in other answers to no avail.
I am trying to stop duplicated array elements by throwing an exception.
for(Result r : studresults)
{
if(r.equals(result))
{
return false;
}
}
EDIT OK HERE IS THE WHOLE CLASS>
package ams.model;
import java.util.ArrayList;
import java.util.Arrays;
import ams.model.exception.EnrollmentException;
public abstract class AbstractStudent implements Student {
private int studentId;
private String studentName;
private ArrayList<Course> studcourses = new ArrayList<Course>();
private ArrayList<Result> studresults = new ArrayList<Result>();
public AbstractStudent(int studentId, String studentName) {
this.studentId = studentId;
this.studentName = studentName;
}
public String getFullName() {
return studentName;
}
public int getStudentId() {
return studentId;
}
public Result[] getResults() {
Result[] res = studresults.toArray(new Result[0]);
if(res.length > 0 )
{
return res;
}
return null;
}
public boolean addResult(Result result)
{
for(Result r : studresults)
{
if(r.equals(result))
{
return false;
}
}
studresults.add(result);
return true;
}
public void enrollIntoCourse(Course c)
{
//for re-enrollment
if(studcourses.contains(c))
{
studcourses.remove(c);
studresults.clear();
}
studcourses.add(c);
}
public void withdrawFromCourse(Course c) throws EnrollmentException
{
if(studcourses.size() > 0)
{
studcourses.remove(c);
}
else
throw new EnrollmentException();
}
public Course[] getCurrentEnrolment()
{
return studcourses.toArray(new Course[0]);
}
public abstract int calculateCurrentLoad();
public int calculateCareerPoints() {
// TODO Auto-generated method stub
return 0;
}
public String toString()
{
return studentId + ":" + studentName +":" + calculateCurrentLoad();
}
}
Do you already override hashCode method in Result?
If you override equals, you have to override the hashCode method also to allow you return the same hashcode for the similar objects (objects which has the same value but actually different object instances).
I think the default implementation of hashcode will returns different value for a different object instances even though they have the same values.
Instead I converted toString and then compared and it works???
Makes me think there was something slightly unidentical before?
New method
public boolean addResult(Result r)
{
for (Result s : studresults)
{
String sr1 = s.toString();
String sr2 = r.toString();
if(sr1.equals(sr2))
{
return false;
}
}

Categories

Resources