Java Object Reference Tasks - java

I need help to figure out a method on how to add elements to my array list. Once added i would like to have it show true or false if the element was added previously, if not then have it show true as in successfully added new Disc. This is a portion of my code so far.
public class MediaArchive
{
private String name ;
private String owner ;
private ArrayList<Disc> list;
//Constructor
public MedieArkiv(String name, String owner) {
this.name = name;
this.owner = name;
list = new ArrayList<Disc>();
}
//Method for adding a new "Disc" to the ArrayList.
public void addDisc(Disc newDisc) {
list.add(newDisc);
}
So my question is, how do i make a method that can add "new Discs" to my array list? And save them as parameters so the list knows if they are added or not by using ture or false to show it.
Sidenote: I am learning this through BlueJ, and am still very fresh at the language.

If you are trying to get a list of unique Disc values, you should first override the equals and hashCode methods of Disc. This allows you to determine whether two different instances of Disc should be considered to have equal values.
Then, replace ArrayList with LinkedHashSet: LinkedHashSet is an implementation of Set (which, by contract, forbids duplicate values) but that also preserves insertion order - so it's a bit like a List without duplicates.
Now LinkedHashSet.add(Disc) would return true if the element is "new", and false if it was already present.

Implementing Collection.add, List.add returns:
true if this collection changed as a result of the call
... (see API).
Note that as mentioned by Andy Turner this will always return true for ArrayList (if an Exception is not being thrown by add).
You could also have a Set instead, and implement equals and hashCode in your Disc class, in case your check is required to ensure no duplicates are being added.
The Set.add method returns:
true if this set did not already contain the specified element
... (see API).

ArrayList has Contains method, you need to pass on the argument so, that it will return true if the value is already added else it will return false.
In your case
if(list.contains(newDisc))
{
//Element already added
}
else
{
list.add(newDisc);
}

Related

When using Collections.sort - no instance of Variable T exist so that Collection conforms etc

so I've build these two classes:
1. Genre which implements Comparable
2. GenreManager which takes a Collection of genres and creates an internal copy of it. Later in GenreManager, I will need to add new Genres by getting a name as an input, and I need to assign this Genre the next free id number, which is basically the next smallest positive number after the smallest used id.
I am trying to use Collections.sort() to sort my list but I am getting the following error:
"no instance(s) of type variable(s) T exist so that Collection conforms to List." and I am not sure what this is referring to... I've tried ready a bunch of posts about this on here but couldn't figure out the solution... Here is part of the code:
public class Genre implements Comparable<Genre>{
private int id;
private String name;
public Genre(int id, String name){
this.id = Validate.requireNonNegative(id);
this.name = Validate.requireNonNullNotEmpty(name);
}
#Override
public int compareTo(Genre o) {
int res = Integer.valueOf(id).compareTo(o.id);
if (res != 0){
return res;
}
else{
return this.name.compareToIgnoreCase(o.name);
}
}
}
public class GenreManager{
private Collection<Genre> genres;
private Collection<Genre> sortedTree;
public GenreManager(){
this.genres = new ArrayList<Genre>();
}
public GenreManager(Collection<Genre> genres){
// check for duplicates
for (Genre x : genres){
for (Genre y : genres){
if (x.equals(y) || x.getName().equals(y.getName()))
throw new IllegalArgumentException("List contains duplicates");
}
}
this.genres = new ArrayList<Genre>(Collections.sort(genres));
}
}
I am trying to do the sorting in the constructor above. Can someone tell me how to go around this?
I tried playing around a little bit, trying to change the private variable from Collection<Genre> to List<Genre> for example and similar things but nothing worked... I also tried casting the input of the .sort method to (List<Genre>) but it didn't work either.
PS: I can't change any of the method header or class headers.
Thanks!
As per request, here's a compilation of my comments to answer the question:
The immediate problem is that Collections.sort(List<T>) takes a List parameter and not just a Collection because collections in general don't have to be sortable (e.g. hash sets aren't). Additionally the method returns void and sorts the passed list in place, i.e. the way you call it won't compile.
Taking all this into consideration your code might be changed to something like this:
public class GenreManager{
private List<Genre> genres;
...
public GenreManager(Collection<Genre> genres){
...
//create a list out of the passed collection
this.genres = new ArrayList<Genre>( genres );
//sort the list
Collections.sort(this.genres);
}
}
The other problem with the code you posted is that for any non-empty collection it will throw the IllegalArgumentException because elements are compared to themselves. Adding a check for x != y to the condition would solve that but the code is still somewhat slow because it has a time complexity of O(n2).
This can be solved to use a set instead of a list. However, a HashSet would depend on how equals() and hashCode() define equality, which doesn't seem to match your requirements. That could be solved by using a wrapper object that implements both methods as needed.
A better approach might be to use a TreeSet though. TreeSet uses comparisons to determine order and equality (if the compare result is 0) and thus would allow you to either let your Genre class implement Comparable as you did or provide a separate Comparator (e.g. if you need multiple different definitions of equality).
If you just want to eliminate duplicates, your code could then look like this:
public class GenreManager{
private SortedSet<Genre> genres;
...
public GenreManager(Collection<Genre> genres){
this.genres = new TreeSet<>( genres );
}
}
If you want to know what duplicates are in the collection you could do it like this:
public GenreManager(Collection<Genre> genres){
this.genres = new TreeSet<>(); //the generic type is inferred from this.genres
for( Genre element : genres ) {
//If the element didn't exist in the set add() will return true, false if it existed
boolean nonDuplicate = this.genres.add( element );
//handle the duplicate element here
}
}
As it was mentioned before, your code has several errors which makes it unusable:
Checking equality of elements with themselves.
Collections.sort method takes a List of Comparable as an argument, when Collection is a little higher in a hierarchy, which means you can't use it as a parameter. To resolve it change declaration of variable genres to List.
method Collections.sort returns void, so you can't pass its return value as an argument to ArrayList constructor. Instead, try assigning genres variable first and then sorting it via Collections.sort as
this.genres = new ArrayList/LinkedList(genres)
Collections.sort(this.genres)
Again, you may consider using TreeSet as it holds all elements sorted and without duplicates, so your constructor will just look like
this.genres = new TreeSet(genres)
In addition, it prevents duplicates even during adding, so if you have 10 elements, adding already existing one won't make any changes to your set. But using this data structure you should check variable for null before adding, as it will produce NullPointerException

Why compiler don't prevent us, when we try to add duplicate object in Hashset?

As I know HashSet not do not take duplicate object, When I try below code it simply add first one and ignore second one.Why there is no checked Exception for this. How add() is implemented for HashSet?
public static void main (String[] args) throws java.lang.Exception
{
HashSet Hs = new HashSet();
Hs.add("A");
Hs.add("A");
System.out.println(Hs);
}
OUTPUT- [A]
The Set interface allows you to attempt to add the same element again and again. It will simply add the element just the first time.
It is not an error to attempt to add the same element multiple times.
boolean java.util.Set.add(E e)
Adds the specified element to this set if it is not already present (optional operation). More formally, adds the specified element e to this set if the set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, the call leaves the set unchanged and returns false. In combination with the restriction on constructors, this ensures that sets never contain duplicate elements.
This allows you to check if an element belongs to the Set and add it to the Set if not with a single statement:
if (set.add(value)) {
.... // will be executed only if the value was added
}
instead of the longer
if (!set.contains(value)) {
set.add(value);
....
}
what happens internally when you pass duplicate elements in the
add() method of the Set object , It will return false and do not add
to the HashSet , as the element is already present .So far so good .
But the main problem arises that how it returns false . So here is the
answer When you open the HashSet implementation of the add() method in
Java Apis that is rt.jar , you will find the following code in it.
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
// SOME CODE ,i.e Other methods in Hash Set
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
// SOME CODE ,i.e Other methods in Hash Set
}
So , we are achieving uniqueness in Set,internally in java through HashMap . Whenever you create an object of HashSet it will create an object of HashMap as you can see in the italic lines in the above code .
When you will try to add value in set and that does not present in to the set at that time it will get added and add method will return the true,but if the value is already present in to set at that time add method will returns false.
set.add() method dont be supposed to give you an error when you try to add a value already in the Set,it will simply return the false.
https://docs.oracle.com/javase/7/docs/api/java/util/HashSet.html#add%28E%29
Checking the duplicate values in the set is the duty of run time but compiler. Compiler does not have any idea on the content of the set while compiling the java code. Hence you won't see any warning, suggestion, etc.
I hope this would answer your question.

when do i need hashcode and equals method?

I have hashset whare i will store set to objects and i want to find particular object, in this case why do i need to override the hashcode and equals method which i read from below example
public class Emp
{
private int age ;
public Emp( int age )
{
super();
this.age = age;
}
public int hashCode()
{
return age;
}
public boolean equals( Object obj )
{
boolean flag = false;
Emp emp = ( Emp )obj;
if( emp.age == age )
flag = true;
return flag;
}
}
They are saying i would get false for the below query if i not override the hashcode and equals method.
System.out.println("HashSet Size--->>>"+hs.size());
System.out.println("hs.contains( new Emp(25))--->>>"+hs.contains(new Emp(25)));
System.out.println("hs.remove( new Emp(24)--->>>"+hs.remove( new Emp(24));
System.out.println("Now HashSet Size--->>>"+hs.size());
I got confuse how this is related to hashcode and equals just checking the contains(anyobject) and remove(anyobject) in hashset .
Can somebody explain me the above scenario?
At the base of your confusion is the concept of identity vs equality, and what does it mean for a set to contain an element. Let me try to explain.
Suppose in some place in your code you have done this:
HashSet<Emp> hs = new HashSet<>();
hs.add(new Emp(32));
In some other place, you want to see whether an employee for age 32 is in the set. How would you do it? You may consider this:
boolean isThere = hs.contains(new Emp(32));
what you're doing here is creating an instance of Emp passing 32 to the constructor, and then passing the instance to contain().
Note that this instance is not the same instance as the one you created when you added to the set. So, the question is: should contains() return true, as this instance is identical to the one you added, or should it return false, as it is not the very same instance?
The result depends on how hashCode() and equals() are implemented for Emp. With the default implementation, equals() returns true only if the instance passed is the very same that is contained (i.e. uses == to compare the instance passed to contains() and the one stored). In this case, it would return false.
To understand hashCode(), you need to understand how a HashSet works.
When you add an element to a HashSet, an index in an array is computed from the element using hashCode() % <size of the array>. The element is then set as value at the corresponding index.
As different elements may end up having the same hashCode(), more elements may be mapped at the same index, in which case a list of collisions is maintained.
So, going back to your case, why do you need to implement hashCode()? because the default implementation will return different numbers for different instances of Ent, event though age may be the same (The implementation is JVM dependent, for example it could return the address in memory of the instance). So, for contain to work, we need to make sure that the same index in the array is computed for the two instances, hence you need to implement it accordingly. for example, in this case, hashCode() could return the age itself.

Why set is not allowed duplicate value, which kind of mechanism used behind them?

I am new to java, i know set is not allowed duplicate value but i don't know why set is not allowed duplicate value, Actually i am doing practically,
Declared one set and add duplicate value but no kind of error is occurring, no compile time error, no run time. why?
Internally SET store element using HASHTABLE ...HASHTABLE is a structure of Key value pairs..Here what the values passed by the SET is treated as Keys of HASHTABLE Internally. keys are unique cannot be duplicated. That is the reason if you pass any duplicate value it return false and does not added to the SET ...
If the adding element return true it will added into SET...Else it return False, that why it won't give any compilation or runtime error and it wont be added to SET
The meaning of "sets do not allow duplicate values" is that when you add a duplicate to a set, the duplicate is ignored, and the set remains unchanged. This does not lead to compile or runtime errors: duplicates are silently ignored.
You can tell that a value is a duplicate by checking the result of add, like this:
Set<String> testSet = new HashSet<String>();
boolean first = testSet.add("hello");
System.out.println(first); // Prints "true"
boolean second = testSet.add("hello");
System.out.println(second); // Prints "false"
Set is not allowed to store duplicated values by definition. If you need duplicated values, use a List. As specified on the documentation of the interface, when you try to add a duplicated value, the method add returns false, not an Exception.
http://docs.oracle.com/javase/7/docs/api/java/util/Set.html
Set (Oracle Documentation)
A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element. As implied by its name, this interface models the mathematical set abstraction.
See: http://docs.oracle.com/javase/7/docs/api/java/util/Set.html
Set (Methematics) - Quoting from wikipedia
In mathematics, a set is a collection of distinct objects, considered as an object in its own right.
Add Method
According to the documentation of the interface, if the element does not exist it is added. Otherwise, nothing changes.
boolean add(E e):
Adds the specified element to this set if it is not already present (optional operation). If this set already contains the element, the call leaves the set unchanged and returns false.
Example Implementation Code: HashSet
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* #param e element to be added to this set
* #return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
So it is well known fact that Set is not allowing duplicate or equal objects but equality of the objects will be taken care by the programmer by giving the desired implementation of equals() and hashCode() methods.
if we are not implementing these two methods set will even allow duplicates also.
In hash based collections in java, first hashCode() method called and then equals() method called if required to check the equality of the objects.
,so bottom line is Set is using Map functionality internally and set values are inserted as keys inside this map internally and map is not allowing duplicate keys,
You can also check the HashSet.java class from Java API where you can find similar map which is actually doing all the things.
Here is small code sample:
class Employee {
private static int equalsCounter;
private static int hashCodeCounter;
private String name;
private int age;
public Employee() {
super();
}
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
#Override
public String toString() {
return "Employee [name=" + name + ", age=" + age + "]";
}
#Override
public int hashCode() {
hashCodeCounter++;
System.out.println("hashCode() invoked : "+hashCodeCounter+" time");
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
equalsCounter++;
System.out.println("equals() invoked: "+equalsCounter+" time");
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
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;
}
}
public class Main {
public static void main(String[] args) {
Set<Employee> mySet = new HashSet<Employee>();
Employee e1 = new Employee("aaa", 30);
Employee e2 = new Employee("aaa", 30);
Employee e3 = new Employee("aaa", 30);
mySet.add(e1); // HashCode() called and equals() not called
mySet.add(e2);// HashCode() called and equals() also called
mySet.add(e3);// HashCode() called and equals() also called
System.out.println(mySet.size());
}
}
In addition of above answers, here is why set does not allow duplicate elements:
When you call add(E e), method of set, it internally call put(E, e) method of HashMap which looks something like this :
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
Hence, the element you are adding to set/HashSet, is internally added to Map as a key. As we need to associate some value with the key in so dummy value(new Object()) PRESENT is passed every time (as Map can contain more than one duplicate values ).
Now if you closely examine the return map.put(e, PRESENT)==null; of add(e, E) method. There can be two possibilities :
if map.put(k,v) returns null ,then
map.put(e, PRESENT)==null; will return true and element will be added.
if map.put(k,v) returns old value for the key ,then
map.put(e, PRESENT)==null; will return false and element will not be added.
Hope this will help in understanding clearly.
Thanks to A2A..
When you pass a duplicate element in the add method of set object, It'll return false and doesn't add it to the set as the element is already present.
Set<Object> set = new HashSet<Object>();
set.add("test");
set.add("test");
If you look at the implementation of HashSet then it looks like following.
public HashSet() {
map = new HashMap<>();
}
Means HashSet internally creates an object of HashMap.
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
If you look at the add method parameter e, then your passed value (test) will be consider as a key in the map and the PRESENT is the dummy object passed as a value.
HashMap put method returns the following
1. null, if the key is unique and added to the map
2. Old value of the key, if key is duplicate
So, when you are adding test element for the first time, HashMap will add the element and return null after that your add method of set will return true. If you add the test element for 2nd time then your HashMap will return Old Value of the Key then in add method of your set will return false as OldValue != null
Hope it'll be helpful..!!
"a set is a collection of distinct objects" ... http://en.wikipedia.org/wiki/Set_%28mathematics%29
When you create a set there can be only unique objects in it by definition. Adding same object twice does not change the set as the element/object is already in the set. This is expected behavior. An example where this behavior is useful is when one wants to find unique elements from a collection of elements (i.e. remove duplicates).
Because that's how sets are defined. An element can only exist once in a set, but that doesn't mean trying to add it a second time should be an error. It's just a no-op. This is fairly clear from the documentation, for instance, for Set#add:
Adds the specified element to this set if it is not already present (optional operation). ... If this set already contains the element, the call leaves the set unchanged and returns false. In combination with the restriction on constructors, this ensures that sets never contain duplicate elements.
Among other things, this lets you happily add to sets without worrying, and know that the result will only ever have unique values in it.
Declared one set and add duplicate value but no any kind of error are occur, no compile time error no run time. Why?
Because it's not an error. But note that you did (or could have) received an indication that the value was already present: The add method's return value (see link above) tells you: "true if this set did not already contain the specified element"
From the documentation, you can get the following:
Adds the specified element e to this set if the set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, duplicate is ignored, leaves the set unchanged and returns false. This ensures that sets never contain duplicate elements.
boolean add(E e): The method returns true if the set did not already contain the specified element.
It throws the following types of exceptions:
UnsupportedOperationException - if the add operation is not supported by this set
ClassCastException - if the class of the specified element prevents it from being added to this set
NullPointerException - if the specified element is null and this set does not permit null elements
IllegalArgumentException - if some property of the specified element prevents it from being added to this set

Java ArrayList: Adding object using string name

This is a very straightforward task, but I feel I'm overlooking something. I have multiple objects that I'm trying to add to an ArrayList, and each of them has an identifying name in the form of a String. I need to be able to find (interact) with the objects in the ArrayList by calling the string name. So I tried this:
In my item class I have:
private String itemName;
public Item(String name)
{
itemName = name;
}
So I can give it a name to be used by the user.
Then in my class that interacts with the object, I create an ArrayList:
private ArrayList<Item> items = new ArrayList<Item>();
I add an object to the arrayList first by it's actual object name, but I need to be able to interact with it using it's String name, so I tried this:
public void removeItem(String itemName)
{
for (int i = 0; i < items.size(); i++)
{
if (items.get(i).toString() == itemName)
{
items.remove(i);
}
break;
}
}
But it's not removing the item. If all of this is confusing, in essence I'm trying to create an OBJECT that I can give a STRING name (like I did with the item above), then have the ability to add the OBJECT to an ArrayList, and then finally be able to remove, or get, or do something with the OBJECTS in the ArrayList by calling the STRING name. I know I need to iterate through the ArrayList, but I can't actually get the object.
Thanks for any help.
You are dong three mistakes here:
You are using items.get(i).toString() which will not give you itemName for your Item. It will just give you a string representation of your Item class, returned by Object class's toString method, if you don't override one. However, this might work, if you have overriden a toString method, and returned the itemName from that. But, that I don't see. And even if you have overriden that, I suggest you to have getter and setter for your itemName field, and use that to return the itemName.
You are comparing strings using == operator, which will not give you correct result. You should always compare the string using equals method.
So, your if statement should look like:
if (items.get(i).getName().equals(itemName))
3rd problem is, you are trying to modify the List that you are iterating upon. This will not work out, and may throw ConcurrentModificationException. You should use Iterator to remove elements from the List while iterating.
See for more details about those two problems, and how to solve them:
How do I compare strings in Java?
Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop
Further, you can consider overriding equals method in your class, and then you can directly compare your instances using equals method.
Now, having pointed out the some logical problems with your code, it's time to point out some design problems.
Given your requirement, it seems like you need to use a HashMap, rather than a List of some custom type storing your attribute. You can create a map like this:
Map<String, Integer> map = new HashMap<String, Integer>();
which will contain the mapping of itemName to respective Item, and then getting the Item for a particular itemName is as simple as map.get(itemName).
It sounds like you should be using a Map for this, for instance java.util.HashMap<String, Item>. The Map interface provides exactly those operations you're looking for, and it is also iterable.
add a getter to your object to get the name, like so:
public class Item {
private final String name; //once given cannot change
public Item(String name) {
this.name = name; //yhis.name to distinguish between 2 variabled both called "name"
}
public String getName() {
return name; //this.name not required as no other variable called "name" is in scope
}
}
then you could find your Item like this:
for (Item item : theList) {
if (item.getName.equals(requiredName)) {
//got you!
}
}
generally speaking, dont ever compare strings with ==. also, if you want to remove an item from a list youre iterating over you have to use the (older) iterator syntax:
Iterator<Item> iter = theList.iterator();
while (iter.hasNext()) {
Item item = iter.next();
if (item.getName.equals(requiredName)) {
//got you!
iter.remove();
break; //no need to go over the rest of the list
}
}
and lastly, if all you want is to look up items by their name a list is not your best collection since finding the item may require traversing the entire list. maps (hashmap specifically) will give you much better performance for this type of operation. you could use the name as the key
my guess is that you items.get(i).toString() does not do what you think it does. Why don't you use some thing like items.get(i).name or create getters or setters for name in Item object and retrieve name by items.get(i).getName()
There is this - the way you implemented the removeItem, you can also do it directly using
ArrayList.remove(item.itemName)- that's just before you get all stuck up with ConcurrentModificationException and reimplementing stuff that already exists - look at the library!! Read the documentation of ArrayList!
For clarification: in Java (and mostly really only in Java): == means comparison of references.
So:
String a = "A";
String b = new StringBuilder("A").toString();
if (a == b) // --> false
if (a.equals(b)) // --> true
You could also consider using org.apache.commons.lang.StringUtils.equals for that - which is safe regarding null pointers.
As others already pointed out - the toString-method will only work correctly if you implement it correctly (returning the name in your case). By original toString returns a class-name together with an ID. That's probably not what you want (simply try to print it out).

Categories