List of Objects to be Unique [duplicate] - java

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.

Related

how to add/remove multiples of objects from an array list

I am trying to build an ArrayList that will contain objects. when i add an object to the list i want it to first check the array list for that object. and if it finds it i want it to increase a quantity variable in that object and not create a new object in the list. and then vice versa when removing objects. I have accomplished a way that works when removing an object. But i dont think i fully understand the methods in the arraylist or the logic when creating and arraylist of objects. as when i use .contains or .equals im not getting the desired effect.
public class ItemBag {
private ArrayList<Item> inventory = new ArrayList<Item>();
public ItemBag() {
}
public void addItem(Item objName, int quantity) {
if (inventory.contains(objName)) {
System.out.println("if statement is true!");
int i = inventory.indexOf(objName);
inventory.get(i).setQuantity(inventory.get(i).getQuantity() + quantity);
} else {
inventory.add(objName);
objName.setQuantity(quantity);
}
}
public void removeItems(String itemName, int quantiy) {
for (int i = 0; i < inventory.size(); i++) {
if (inventory.get(i).name() == itemName) {
inventory.get(i).setQuantity(inventory.get(i).getQuantity() - quantiy);
if (inventory.get(i).getQuantity() <= 0) {
inventory.remove(inventory.get(i));
}
}
}
}
public void showInventory() {
for (int i = 0; i < inventory.size(); i++) {
System.out.println(inventory.get(i).name() + " : " + inventory.get(i).getQuantity());
}
}
then when creating the itemBag in another object i am writing
ItemBag merchantItems = new ItemBag();
public void merchantBob() {
merchantItems.addItem(new HealthPotion() ,3);
merchantItems.showInventory();
System.out.println("add 1");
merchantItems.addItem(new HealthPotion(),1);
merchantItems.showInventory();
Items class
package Items;
public abstract class Item {
private int quantity = 0;
public Item() {
}
public abstract String name();
public abstract int cost();
public abstract String type();
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
HealthPotion Class
public class HealthPotion extends Potions {
protected int addHealth = 10;
#Override
public int drinkPotion() {
return addHealth;
}
#Override
public String name() {
return "Health Potion";
}
#Override
public int cost() {
return 5;
}
#Override
public String type() {
return "Potion";
}
}
The .contains() method would iterate through the list and use .equals() method to compare each element and check if the provided object exists in the list.
.equals() method would compare the object reference (unless .equals() is overridden) to check if the objects are same.
For reference: https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#contains-java.lang.Object-
You can override the .equals() method to compare the values of the provided object in the following way:
public abstract class Item {
private int quantity = 0;
public Item() {
}
public abstract String name();
public abstract int cost();
public abstract String type();
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
#Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
Item providedItem = (Item) object;
return name == providedItem.name
&& cost == providedItem.cost
&& type == providedItem.type;
}
}
This should work

Avoiding adding duplicate objects to an ArrayList

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.

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).

Get Common Element in Two Lists of Book

I have two array lists that store instances of a class called Book. I am trying to get the book/books that is inside both lists.
This is a search feature that allows you to search for a book by entering the book's ISBN, Name and Author. The list 'resultA' contains the books with the inputted ISBN and Name while the other list 'resultB' contains the books written by the inputted author. To get the final result I need to get the book that is inside both arrays.
I have tried using the retainAll() function but I found that it doesn't work on lists with instances stored.
List<Book> resultA = BookManager.getBooksWhere("book_ISBN", ISBN, "book_name", bookName);
List<Book> resultB = BookManager.getBooksByAuthors(authors);
resultB.retainAll(resultA);
searchResults = resultA;
Is there some other function I can use instead to get the common book?
(Update)
Sorry, Here is the Book class:
public class Book
{
private int bookID;
private String bookISBN;
private String category;
private int libId;
private String name;
#Override
public String toString()
{
String output = bookISBN + " - " + name + " - " + category + " - ";
return output;
}
public int getBookID()
{
return bookID;
}
public void setBookID(int bookID)
{
this.bookID = bookID;
}
public String getBookISBN()
{
return bookISBN;
}
public void setBookISBN(String bookISBN)
{
this.bookISBN = bookISBN;
}
public int getLibId()
{
return libId;
}
public void setLibId(int libId)
{
this.libId = libId;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
(Update)
I did not know that I had to override the Books class for this to work and thanks for pointing that out DNA and pbabcdefp. I have looked up on how to do it and it has worked correctly, the common book is being taken out from both lists.
This was inserted in the book class and uses their unique id to compare if books are equal.
#Override
public boolean equals(Object o)
{
if (o == null)
return false;
if (getClass() != o.getClass())
return false;
final Book otherBook = (Book) o;
if (this.bookId != otherBook.bookId)
{
return false;
}
return true;
}
Assuming you defined an equals function for the Book class, here is a function that can get the common elements in two arrays:
public static <T> List<T> getCommonElements(List<T> list1, List<T> list2) {
List<T> resultsList = new ArrayList<>();
for (T element1: list1) {
for (T element2: list2) {
if (element1.equals(element2)) {
resultsList.add(element2);
}
}
}
return resultsList;
}
This looks like a school question. With that, I doubt you are looking for an answer with generics or comparators or overriding the compareTo or equal method.
Hence, this is what you can do:
for(int x=0; x<listA.size(); x++)
for(int y=0; y<listB.size(); y++)
if(listA.get(x).getISBN().equals(listB.get(y).getISBN()))
return listA.get(x);
Instead of using equals to compare, you get use their ISBN which is supposed to be their unique id. Alternatively, you can override the equals method within the Book class to compare the ISBN.

.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