This is regarding the usage of ArrayList returned by another class instance variable.
Class A {
//assigned list of string to it.
private List < String > newAl;
//returns the list
public List < String > getList() {
return newA1;
}
}
Class Test {
public void go() {
List < String > list = a.getList();
list.add("");
}
}
In the Test class when i retreive list and manipulate the list.Because of the reference ,class A list also got manipulated.If A is part of third party code.How do I correct my code in Test class so that original object wouldnt be affected?
The ArrayList constructor takes a Collection so you can use that:
List<String> list = new ArrayList(a.getList());
I think it's better to do it like this, but depending on what you're doing, you may want to construct the new List in the getter. That also helps type hiding.
Related
This seems very simple but I can't quite figure out why this isn't working.
I want to reverse the elements in my LinkedList which I have a working method for, but I can't return the value as my prof wants it to be a void method. How would I go about this?
import java.util.LinkedList;
public class ListUtil {
public static void reverse(LinkedList<String> strings) {
LinkedList<String> reverseLinkedList = new LinkedList<>();
for(int i = strings.size() - 1; i >= 0; i--) {
reverseLinkedList.add(strings.get(i));
}
strings = reverseLinkedList;
System.out.println(strings);
}
}
import java.util.LinkedList;
public class ReverseTester {
public static void main(String[] args) {
LinkedList<String> employeeNames = new LinkedList<>();
employeeNames.addLast("Dick");
employeeNames.addLast("Harry");
employeeNames.addLast("Romeo");
employeeNames.addLast("Tom");
ListUtil.reverse(employeeNames);
System.out.println(employeeNames);
System.out.println("Expected: [Tom, Romeo, Harry, Dick]");
}
}
In my ListUtil class, it does reverse the list, but doesnt return a value (as it is void) but I don't know how to go about setting employeeName in the ReverseTester class.
I know this is probably super simple but I have not been able to figure this out for the life of me, any help is greatly appreciated.
Empty and re-fill the existing list rather than replacing it.
public static void reverse(LinkedList<String> strings) {
List<String> temp = new ArrayList<>(strings); // Copy the contents of the original list. Pass the original list to constructor of our duplicate list.
strings.clear(); // Empty the original list.
for (String e : temp)
strings.addFirst(e); // Refill the original list using elements from our duplicate list.
}
Or simply
public static void reverse(LinkedList<String> strings) {
Collections.reverse(strings);
}
Non-primitive Java object are stored by reference so you don't need to return anything from ListUtil::reverse. Any changes made to the object in the function will be reflected in ReverseTester.java. This happens because, again, non-primitive Java objects are stored by reference. Basically your code does exactly what you want it to do. You make a LinkedList, populate it with items, and then reverse those items.
You will have a problem with System.out.println(employeeNames); though. Because that will just print the object's formal name and not it's contents. If you want to print the contents of a list in Java you can do:
for (String name : employeeNames) {
System.out.println(t);
}
This is my first answer so please ask any questions if I wasn't clear enough!
I have a class (A.java) that contains two private fields of type ArrayList and HashMap.
I also have another class (B.java) that should have access to their data. I could make two getters, but I don't want to return my collections as is. Class B.java should only have access to data, not to add(), isEmpty(), containsKey() etc.
Can I return my collections in such way, so I could somehow use it with foreach in class B somehow but without giving the possibility to modify them?
Don't return a collection, return a Stream. That way it is easy for the user to know that they are getting a stream of objects, not a collection. And it's easy to change the implementation of the collection without changing the way it's used. It's trivial for the user to filter, map, reduce collect etc.
So:
class A {
private List<C> cs = new ArrayList<>();
public Stream<C> getCs() {
return cs.stream();
}
}
class B {
public void processCs(A a) {
a.getCs().filter(C::hasFooness).forEach(...);
}
}
Create a getter method that returns a "Collections.unmodifiableList()" like this:
List<String> strings = new ArrayList<String>();
List<String> unmodifiable = Collections.unmodifiableList(strings);
unmodifiable.add("New string"); // will fail at runtime
strings.add("Aha!"); // will succeed
System.out.println(unmodifiable);
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
Without generics it is possible to create an ArrayList with elements of different types. I want to iterate through it and put out the elements. I can not use a for-each-loop, because it wants a specific type. I tried Iterator but wasn't successful.
So I have two questions:
Is it possible to iterate through an array list and put out (e. g. with System.out.println) all elements no matter of which type they are?
Is it possible to iterate through an array list and put out only the elements which are of a specific type (e. g. only the Strings)?
Sure!
The toString method is defined on the Object class. The Object class is the base class of every user-defined class. You could easily write:
for (Object item: itemList) {
// prints all items
System.out.println(item);
if (item instanceof YourDesiredClass) {
YourDesiredClass specificItem = (YourDesiredClass) item;
//doSomethingElse(specificItem)
}
}
Is it possible to iterate through an array list and put out (e. g.
with System.out.println) all elements no matter of which file type
they are?
Yes, You can use the Object class
List<Object> myList = new ArrayList<>();
myList.add("Hello World"); // string
myList.add(Math.PI); // a double
myList.add(188); // an int
myList.add(9099099999999L); // a Long
// for java8
myList.forEach(System.out::println);
//before java8
for (Object object : myList) {
System.out.println(object);
}
Is it possible to iterate through an array list and put out only the
elements which are of a specific file type (e. g. only the strings)?
Yes, you can use the iterator and get the Object checking it against the class you need....
Iterator<Object> it = myList.iterator();
while (it.hasNext()) {
Object x = it.next();
if (x instanceof String) {
System.out.println("this is a String: " + x);
}
}
As far as I know, yes.
You can make ArrayList which contains Objects (see Java class Object), because each class you define in Java at least extends class Object which is a top class.
Now let me answer your questions:
yes, it is. Each object in the list knows which class it is instance of and has method toString(). When you swipe through ArrayList and call toString() for every object, the most specific method toString() will be called. For example, if it's instance of Integer (let's say it's called number) and you casted it to Object, call number.toString();, although compiler now looks at that number as the Object, it will call toString() method from Integer class. That's called dynamic polymorphism
yes, you can check which class is the Object instance of. Each of these objects has that info; casting it to Object class is just like saying to compiler "here is some object, I want you to look at it as an instance of class Object" - just like putting glasses to a compiler.
And object knows which class it is, so you can just ask, for example:
if(myObject instanceof String){
//do something;
}
Hope it helped, I tried to explain it the best way I could so you understand what's going on "under the hood" :)
Just object
new ArrayList<Object>().iterator().forEachRemaining(element -> {
System.out.println(element);
});
A specific type
new ArrayList<Object>().stream().filter(element -> element instanceof String).iterator()
.forEachRemaining(System.out::println);
Edit: this answer requires Java 8
Is it possible to iterate through an array list and put out (e. g. with System.out.println) all elements no matter of which file type they are?
Sure, you can iterate a list (or arraylist) of Objectclass and do what you need.
Is it possible to iterate through an array list and put out only the elements which are of a specific file type (e. g. only the strings)?
Yes, you can use instanceof and do specific actions for specific classes.
Usage example:
List<Object> genericList = new ArrayList<>();
genericList.add("test");
genericList.add(2);
genericList.add('c');
for (Object object: genericList) {
// "Put" out object (question 1)
System.out.println(object);
// Check object type (question 2)
if (object instanceof AnyClass) {
//doSomething()
}else if (object instanceof AnotherClass){
//doSomethingElse()
}
}
You can always use a Type all the Objects have in common. The last one will always be Object, since every Class extends Object.
But since we don't like to cast it's mostly the better approach to build a basic class for that:
public abstract class FileType
{
public abstract String getTypeName();
public abstract String getTypeDescription();
}
public class JSON extends FileType
{
#Override
public String getTypeName()
{
return "JSON";
}
#Override
public String getTypeDescription()
{
return "JavaScript Object Notation";
}
}
public class TXT extends FileType
{
#Override
public String getTypeName()
{
return "TXT";
}
#Override
public String getTypeDescription()
{
return "Textfile";
}
}
Now you can make a List of FileType's and use the Methods of it:
List<FileType> fileTypes = new ArrayList<>();
list.add(new JSON()); // JSON fits good in here
list.add(new TXT()); // TXT also
for (FileType fileType : list)
{
System.out.println(fileType.getTypeName()); // have the FileType-Methods savely available
}
I want to know if there's a way to run a method of inside an element of an Arraylist
but I don't want to do it with get because it actually changes fields of the element
thanks
benny.
You don't want to do it with get as in yourList.get(5).someMethod()?
The get method will not "extract" the element it returns, it will only return a copy of the reference. Getting + removing is the implementaiton of remove.
So, unless you have overridden the get method it will not modify the list.
Update and clarification:
List<String> list = new ArrayList<String>();
list.add(myObject); // add a reference to myObject in the list
// (remember, you can't pass around objects in java)
list.get(0).someMethod(); // get a copy of that reference and call someMethod()
Just to make everything even more clear than all the comments did:
public class ReferenceTester {
public static void main(final String[] args) {
final String original = "The One and only one";
final List<String> list = new ArrayList<String>();
list.add(original);
final String copy = list.get(0);
if (original == copy) {
System.out.println("Whoops, these are actually two references to the same object.");
} else {
System.out.println("References to a different objects? A copy?");
}
}
}
Run this class and see what it prints.