List Interface (adding linkedlist to a list of lists) - java

I just came across this code that works
List<List<Integer>> parentList = new ArrayList<>();
LinkedList<Integer> curList = new LinkedList<>();
parentList.add(curList);
I guess I was a bit thrown off because I didn't expect you'd be able to add a LinkedList to something declared as a List. I thought you would have had to do something like
List<Integer>curList = new LinkedList<>();
parentList.add(curList);
I thought the whole point of declaring / initializing a LinkedList or ArrayList as a List was so that you could add them to a List even though they are implemented as LinkedLists and ArrayLists.
edit: Or am I getting confused with the fact that we do this as an abstraction layer - we declare it as a List so that even if its made up of arraylists and linkedlists, we can still use the same List interface functions on all of them. And we can add linkedlists to a List of Lists because linkedlists are just implementing the List interface

LinkedList extends AbstractSequentialList which in turns extends AbstractList and ArrayList extends AbstractList.
So both LinkedList and ArrayList underlying extend the same subclass. The add method on a List interface has a restriction to add elements of similar type. Therefore both LinkedList and ArrayList are considered to be similar and therefore are allowed to be added.
Also logically adding the LinkedList doesn't changes the behavior of elements inside it as the order is maintained even after getting added to the list.

tl;dr
This:
LinkedList < Integer > curList = new LinkedList<>() ; // <-- Declared as `LinkedList`.
parentList.add( curList ) ;
… and this:
List < Integer > curList = new LinkedList<>() ; // <-- Declared as `List`.
parentList.add( curList ) ;
… have the same effect.
In both cases, parentList is expecting to receive a List that holds Integer objects. A LinkedList is simultaneously both a LinkedList and a List. So passing curList defined as a List or defined as a LinkedList makes no difference. In both cases a List is received by the parentList.add method.
Assigning a LinkedList object to a List variable does not change the nature of the object.
List means any kind of List implementation: ArrayList, LinkedList, whatever
Your first collection, parentList, is a list of lists.
Each element within that list is itself another list. The declaration List< List < Integer > > parentList tells us that we have a list of lists of Integer objects.
Both the outer list and the inner lists can be any kind of List, not necessarily an ArrayList or LinkedList.
Your second collection, curList, is simply a list of Integer objects.
We know this from its declaration, LinkedList < Integer > curList.
In this declaration, we are saying the list is definitely a LinkedList, not any other kind of List.
Here is a full example.
First we make a List object holding lists of Integer objects. This outer list is empty.
Then we add a list of three single-digit odd numbers (3, 5, 9). This non-modifiable list is generated by the List.of method. The concrete implementation of List used for the object returned by List in not promised, and may vary with the situation and the version of Java. All we know for sure is that we are getting some implementation of List that holds Integer objects.
Then we create a LinkedList holding Integer objects. After adding a couple of double-digit numbers, we add the list to the outer list.
Here are the key points to this code that seem to be confusing you:
We could have declared this curList variable as the more general List < Integer > curList rather than the more specific LinkedList < Integer > curList. The type of List used for curList is irrelevant. The outer list parentList has said it does not care about which List implementation is added as an element, only that the added element be some kind of List that holds Integer objects.
We could decide that the outer list would be better served if it were a LinkedList rather than an ArrayList. If we so decide, we could simply change List< List < Integer > > parentList = new ArrayList<>(); to List< List < Integer > > parentList = new LinkedList<>(); — and none of the other code would break. We can switch new ArrayList to new LinkedList without changing any other code. That is the benefit of polymorphism in OOP.
/* package whatever; // don't place package name! */
import java.util.*;
import java.lang.*;
import java.io.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
List< List < Integer > > parentList = new ArrayList<>();
parentList.add( List.of( 3 , 5 , 9 ) ) ; // `List.of` returns *some* kind of (unmodifiable ) `List`; we do not know the concrete class nor do we care.
LinkedList < Integer > curList = new LinkedList<>();
curList.add( 21 ) ;
curList.add( 42 ) ;
parentList.add(curList);
System.out.println( parentList ) ;
}
}
See this code run live on IdeOne.com.
[[3, 5, 9], [21, 42]]
In that output, we see a collection, a List that contains two elements. Each element is itself a List, a list of Integer element. The first element of the outer list holds a reference to a list of three Integer objects as elements, while the second element of the outer list holds a reference to a list of two Integer objects as elements.
You said:
I guess I was a bit thrown off because I didn't expect you'd be able to add a LinkedList to something declared as a List.
That something (parentList) is declared as a list of lists. The LinkedList you are passing is a List, so it fits, it is a matching type. Any LinkedList is also a List.
If you wanted the outer list (parentList) to hold only ArrayList objects rather than any kind of List, you would have declared it as:
List< ArrayList < Integer > > parentList = new ArrayList<>();
With such a declaration, passing new LinkedList<>() to its add method would have resulted in a compiler error. Square peg, round hole.
But you would only make a declaration if your code later has a need for methods that are exclusive to ArrayList class, that are not defined in the more general List interface. Generally, you should use the most general interface or superclass that meets your needs, rather than the more specific concrete class or sub-interface. If List has the methods your later code needs, then declare List< List < Integer > > parentList. This gives you flexibility.
You said:
I thought you would have had to do something like
List<Integer>curList = new LinkedList<>();
parentList.add(curList);
When you assign a LinkedList object to a List variable, you are not changing anything. A LinkedList object is simultaneously both a ListedList and a List.
When you pass curList to the parentList.add method, all that method sees is a List. Whether you declare curList as a List or LinkedList does not matter to the add method. The compiler checks that whatever is being passed into add is (a) some kind of List implementation, any List implementation, and (b) holds Integer objects.

Related

Created two Lists from same array, Modifying one List, changes the other

I created two list from same array and sorted one of them. When I tried to change one list, other list also got updated.
List<Integer> list = Arrays.asList(ar);
List<Integer> sorted = Arrays.asList(ar);
Collections.sort(sorted);
list.set(0,10000000); //changes sorted also
It took me a while figure out, below mentioned code worked.
List<Integer> sorted = new ArrayList<Integer>(Arrays.asList(ar));
I want to know why my first approach didn't work? I created two separate lists, why the changes are taking place in both of them. How does java assign values to variables here?
From the Java documentation for Arrays.asList:
Returns a fixed-size list backed by the specified array. (Changes to the returned list "write through" to the array.) This method acts as bridge between array-based and collection-based APIs, in combination with Collection.toArray(). The returned list is serializable and implements RandomAccess.
So when you change something in list, it "writes through" to the underlying array, ar, which is also the underlying array in sorted, so the change is reflected in sorted as well.
Also, the code for asList is:
public static <T> List<T> asList(T... a) {
return new ArrayList<T>(a);
}
This is java.util.Arrays.ArrayList, which has the following definition:
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
What is important is that a is not copied, it is the original array. The java.util.ArrayList class has the following constructor
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
so in the java.util.ArrayList constructor, we create copies of each element, and in java.util.Arrays.ArrayList, we do not.
Arrays has its own implementations of ArrayList which does not make a copy of array from toList
A List is a collection of objects, and both list are collections of the same objects. The set statement changes an object, and the object is shared by both lists.
I don't understand why the second version works.
list & sorted are still pointing to the same memory address of ar in the first approach while in the second approach a new memory address has been allocated after calling a constructor a new memory block is allocated to the class object.

Difference between Arrays.asList(array) and new ArrayList<Integer>(Arrays.asList(array))

What is the difference between
List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia)); // Copy
List<Integer> list2 = Arrays.asList(ia);
, where ia is an array of integers?
I came to know that some operations are not allowed in list2. Why is it so?
How is it stored in memory (references / copy)?
When I shuffle the lists, list1 doesn't affect the original array, but list2 does. But still list2 is somewhat confusing.
How does ArrayList being upcasted to list differ from creating a new ArrayList?
list1 differs from (1)
ArrayList<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));
First, let's see what this does:
Arrays.asList(ia)
It takes an array ia and creates a wrapper that implements List<Integer>, which makes the original array available as a list. Nothing is copied and all, only a single wrapper object is created. Operations on the list wrapper are propagated to the original array. This means that if you shuffle the list wrapper, the original array is shuffled as well, if you overwrite an element, it gets overwritten in the original array, etc. Of course, some List operations aren't allowed on the wrapper, like adding or removing elements from the list, you can only read or overwrite the elements.
Note that the list wrapper doesn't extend ArrayList - it's a different kind of object. ArrayLists have their own, internal array, in which they store their elements, and are able to resize the internal arrays etc. The wrapper doesn't have its own internal array, it only propagates operations to the array given to it.
On the other hand, if you subsequently create a new array as
new ArrayList<Integer>(Arrays.asList(ia))
then you create new ArrayList, which is a full, independent copy of the original one. Although here you create the wrapper using Arrays.asList as well, it is used only during the construction of the new ArrayList and is garbage-collected afterwards. The structure of this new ArrayList is completely independent of the original array. It contains the same elements (both the original array and this new ArrayList reference the same integers in memory), but it creates a new, internal array, that holds the references. So when you shuffle it, add, remove elements etc., the original array is unchanged.
Well, this is because ArrayList resulting from Arrays.asList() is not of the type java.util.ArrayList.
Arrays.asList() creates an ArrayList of type java.util.Arrays$ArrayList which does not extend java.util.ArrayList, but only extends java.util.AbstractList.
List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia)); //copy
In this case, list1 is of type ArrayList.
List<Integer> list2 = Arrays.asList(ia);
Here, the list is returned as a List view, meaning it has only the methods attached to that interface. Hence why some methods are not allowed on list2.
ArrayList<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));
Here, you are creating a new ArrayList. You're simply passing it a value in the constructor. This is not an example of casting. In casting, it might look more like this:
ArrayList list1 = (ArrayList)Arrays.asList(ia);
First of all, the Arrays class is a utility class which contains a number of utility methods to operate on Arrays (thanks to the Arrays class. Otherwise, we would have needed to create our own methods to act on Array objects)
asList() method:
asList method is one of the utility methods of Array class, it is a static method that's why we can call this method by its class name (like Arrays.asList(T...a) )
Now here is the twist. Please note that this method doesn't create new ArrayList object. It just returns a List reference to an existing Array object (so now after using asList method, two references to existing Array object gets created)
and this is the reason. All methods that operate on List object, may not work on this Array object using the List reference. Like
for example, Arrays size is fixed in length, hence you obviously can not add or remove elements from Array object using this List reference (like list.add(10) or list.remove(10);. Else it will throw UnsupportedOperationException).
any change you are doing using a list reference will be reflected in the exiting Arrays object (as you are operating on an existing Array object by using a list reference)
In the first case, you are creating a new Arraylist object (in the second case, only a reference to existing Array object is created, but not a new ArrayList object), so now there are two different objects. One is the Array object and another is the ArrayList object and there isn't any connection between them (so changes in one object will not be reflected/affected in another object (that is, in case 2, Array and Arraylist are two different objects)
Case 1:
Integer [] ia = {1,2,3,4};
System.out.println("Array : "+Arrays.toString(ia));
List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia)); // new ArrayList object is created , no connection between existing Array Object
list1.add(5);
list1.add(6);
list1.remove(0);
list1.remove(0);
System.out.println("list1: " + list1);
System.out.println("Array: " + Arrays.toString(ia));
Case 2:
Integer [] ia = {1,2,3,4};
System.out.println("Array: " + Arrays.toString(ia));
List<Integer> list2 = Arrays.asList(ia); // Creates only a (new) List reference to the existing Array object (and NOT a new ArrayList Object)
// list2.add(5); // It will throw java.lang.UnsupportedOperationException - invalid operation (as Array size is fixed)
list2.set(0,10); // Making changes in the existing Array object using the List reference - valid
list2.set(1,11);
ia[2]=12; // Making changes in the existing Array object using the Array reference - valid
System.out.println("list2: " + list2);
System.out.println("Array: " + Arrays.toString(ia));
An explanation with documentation references would be better for someone looking for answer.
1. java.util.Arrays
This is a utility class with bunch of static methods to operate on given array
asList is one such static method that takes input array and returns an object of java.util.Arrays.ArrayList which is a static nested class that extends AbstractList<E> which in turn implements List interface.
So Arrays.asList(inarray) returns a List wrapper around the input array, but this wrapper is java.util.Arrays.ArrayList and not java.util.ArrayList and it refers to the same array, so adding more elements to the List wrapped array would affect the original one too and also we cannot change the length.
2. java.util.ArrayList
ArrayList has a bunch of overloaded constructors
public ArrayList() - // Returns arraylist with default capacity 10
public ArrayList(Collection<? extends E> c)
public ArrayList(int initialCapacity)
So when we pass the Arrays.asList returned object, i.e., List(AbstractList) to the second constructor above, it will create a new dynamic array (this array size increases as we add more elements than its capacity and also the new elements will not affect the original array) shallow copying the original array (shallow copy means it copies over the references only and does not create a new set of same objects as in original array)
String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.List<String> namesList = Arrays.asList(names);
or
String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.List<String> temp = Arrays.asList(names);
The above statement adds the wrapper on the input array. So the methods like add and remove will not be applicable on the list reference object 'namesList'.
If you try to add an element in the existing array/list then you will get "Exception in thread "main" java.lang.UnsupportedOperationException".
The above operation is readonly or viewonly.
We can not perform add or remove operation in list object.
But
String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.ArrayList<String> list1 = new ArrayList<>(Arrays.asList(names));
or
String names[] = new String[]{"Avinash","Amol","John","Peter"};
java.util.List<String> listObject = Arrays.asList(names);
java.util.ArrayList<String> list1 = new ArrayList<>(listObject);
In the above statement you have created a concrete instance of an ArrayList class and passed a list as a parameter.
In this case, methods add and remove will work properly as both methods are from ArrayList class, so here we won't get any UnSupportedOperationException.
Changes made in the Arraylist object (method add or remove an element in/from an arraylist) will get not reflect in to the original java.util.List object.
String names[] = new String[] {
"Avinash",
"Amol",
"John",
"Peter"
};
java.util.List < String > listObject = Arrays.asList(names);
java.util.ArrayList < String > list1 = new ArrayList < > (listObject);
for (String string: list1) {
System.out.print(" " + string);
}
list1.add("Alex"); // Added without any exception
list1.remove("Avinash"); // Added without any exception will not make any changes in original list in this case temp object.
for (String string: list1) {
System.out.print(" " + string);
}
String existingNames[] = new String[] {
"Avinash",
"Amol",
"John",
"Peter"
};
java.util.List < String > namesList = Arrays.asList(names);
namesList.add("Bob"); // UnsupportedOperationException occur
namesList.remove("Avinash"); // UnsupportedOperationException
Note that, in Java 8, 'ia' above must be Integer[] and not int[]. Arrays.asList() of an int array returns a list with a single element. When using the OP's code snippet, the compiler will catch the issue, but some methods (e.g., Collections.shuffle()) will silently fail to do what you expect.
Many people have answered the mechanical details already, but it's worth noting:
This is a poor design choice, by Java.
Java's asList method is documented as "Returns a fixed-size list...". If you take its result and call (say) the .add method, it throws an UnsupportedOperationException. This is unintuitive behavior! If a method says it returns a List, the standard expectation is that it returns an object which supports the methods of interface List. A developer shouldn't have to memorize which of the umpteen util.List methods create Lists that don't actually support all the List methods.
If they had named the method asImmutableList, it would make sense. Or if they just had the method return an actual List (and copy the backing array), it would make sense. They decided to favor both runtime-performance and short names, at the expense of violating both the principle of least astonishment and the good object-oriented practice of avoiding UnsupportedOperationExceptions.
(Also, the designers might have made a interface ImmutableList, to avoid a plethora of UnsupportedOperationExceptions.)
package com.copy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class CopyArray {
public static void main(String[] args) {
List<Integer> list1, list2 = null;
Integer[] intarr = { 3, 4, 2, 1 };
list1 = new ArrayList<Integer>(Arrays.asList(intarr));
list1.add(30);
list2 = Arrays.asList(intarr);
// list2.add(40); Here, we can't modify the existing list,because it's a wrapper
System.out.println("List1");
Iterator<Integer> itr1 = list1.iterator();
while (itr1.hasNext()) {
System.out.println(itr1.next());
}
System.out.println("List2");
Iterator<Integer> itr2 = list2.iterator();
while (itr2.hasNext()) {
System.out.println(itr2.next());
}
}
}
Arrays.asList()
This method returns its own implementation of List. It takes an array as an argument and builds methods and attributes on top of it, since it is not copying any data from an array but using the original array this causes alteration in original array when you modify list returned by the Arrays.asList() method.
On the other hand, ArrayList(Arrays.asList());
is a constructor of ArrayList class which takes a list as argument and returns an ArrayList that is independent of list, i.e., Arrays.asList() in this case passed as an argument.
That is why you see these results.
1.List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia)); //copy
2.List<Integer> list2 = Arrays.asList(ia);
In line 2, Arrays.asList(ia) returns a List reference of inner class object defined within Arrays, which is also called ArrayList but is private and only extends AbstractList. This means what returned from Arrays.asList(ia) is a class object different from what you get from new ArrayList<Integer>.
You cannot use some operations to line 2 because the inner private class within Arrays does not provide those methods.
Take a look at this link and see what you can do with the private inner class:
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/Arrays.java#Arrays.ArrayList
Line 1 creates a new ArrayList object copying elements from what you get from line 2. So you can do whatever you want since java.util.ArrayList provides all those methods.
In response to some comments asking questions about the behaviour of Arrays.asList() since Java 8:
int[] arr1 = {1,2,3};
/*
Arrays are objects in Java, internally int[] will be represented by
an Integer Array object which when printed on console shall output
a pattern such as
[I#address for 1-dim int array,
[[I#address for 2-dim int array,
[[F#address for 2-dim float array etc.
*/
System.out.println(Arrays.asList(arr1));
/*
The line below results in Compile time error as Arrays.asList(int[] array)
returns List<int[]>. The returned list contains only one element
and that is the int[] {1,2,3}
*/
// List<Integer> list1 = Arrays.asList(arr1);
/*
Arrays.asList(arr1) is Arrays$ArrayList object whose only element is int[] array
so the line below prints [[I#...], where [I#... is the array object.
*/
System.out.println(Arrays.asList(arr1));
/*
This prints [I#..., the actual array object stored as single element
in the Arrays$ArrayList object.
*/
System.out.println(Arrays.asList(arr1).get(0));
// prints the contents of array [1,2,3]
System.out.println(Arrays.toString(Arrays.asList(arr1).get(0)));
Integer[] arr2 = {1,2,3};
/*
Arrays.asList(arr) is Arrays$ArrayList object which is
a wrapper list object containing three elements 1,2,3.
Technically, it is pointing to the original Integer[] array
*/
List<Integer> list2 = Arrays.asList(arr2);
// prints the contents of list [1,2,3]
System.out.println(list2);
Summary of the difference -
When a list is created without using the new, the operator Arrays.asList() method returns a wrapper which means:
you can perform an add/update operation.
the changes done in the original array will be reflected to List as well and vice versa.

How do I extract the K "smallest" elements from a list of objects?

I'm looping through a List to find a particular entry, then assigning that to a variable and trying to remove it later. It's easier to demo than to explain.
ArrayList<Example> list1 = populate();
Example ex1 = list1.get(0);
Example ex2 = ex1;
list1.remove(ex2);
I know this likely has something to do with Java's inability to handle pointers, but a viable solution would be great.
Edit: To elaborate, this is a brief example of my code rather than giving you the full thing. What I'm doing is iterating through a list to find the lowest 10 numbers. My technique is to go through the list, find the lowest and add it to another list, then remove that number from the original list and repeat. But my list is made of objects which have an int value inside them, rather than a list of integers.
for(0 to 9){
for(0 to list.size){
if(list.get(x) < smallest)
smallest = list.get(x)
}
smallestList.add(smallest);
list.remove(smallest)
}
I would sort the list. Then, I would create a list with those 10 smallest objects and change the original list list1 to contain the remaining objects. Something like:
Collection.sort(list1);
ArrayList<Example> yourSmallestElements = (ArrayList<Example>)(list1.sublist(0, 9).clone());
list1.removeAll(yourSmallestElements);
NOTE: I cloned the sublist because sublist() only returns a view of the list list1, and that's not what you want here.
Your class Example can implement "Comparable" so that you can define how they need to be compared. You will need to implement the method compareTo(). Something like this:
public class Example implements Comparable<Example> {
private int integerVal = <a value>;
public int compareTo(Example exampleObject) {
return exampleObject.integerVal - this.integerVal;
}
}
Have a look at this link, more precisely the class that begins as follows:
public class Fruit implements Comparable<Fruit>{
If you want to sort your objects...
Example e;
int min=-1; // assuming the list has +ve numbers only
for (Example elem : yourList)
{
if ( elem.gtVaribale() <= min ) //assuming you have variable field in your object
{
e = elem;
min = elem.getVariable();
}
}
yourList.remove(e);
//repeat this for remaining elements of the list
//you can create another sorted list, and do sortedList.add(e), so that sortedList
//have objects in ascending order (of the variable you want to sort) of objects you had in yourList
This is just a pseudoCode and I have not compiled it.
Here you will have to override the comparable method for class Example.
You have to let compiler know which way it should compare your e variable to its list's elements so as to remove it.

Linked List of Linked Lists in Java

I would like to know how to create a linked list of linked lists. Also, It would be helpful if the predefined LinkedList (class from Java) and its methods are used for defining and for other add, get, listIterating operations.
You can put any object in a list, including another list.
LinkedList<LinkedList<YourClass>> list = new LinkedList<LinkedList<YourClass>>();
is a LinkedList of LinkedLists of YourClass objects. It can also be written in a simplified way since Java 7:
LinkedList<LinkedList<YourClass>> list = new LinkedList<>();
Very simple examples of manipulating such a list:
You then need to create each sublist, here adding a single sublist:
list.add(new LinkedList<YourClass>());
Then create the content objects:
list.get(sublistIndex).add(new YourClass());
You can then iterate over it like this (sublists' items are grouped by sublist):
for(LinkedList<YourClass> sublist : list) {
for(YourClass o : sublist) {
// your code here
}
}
If you want to add specific methods to this list of lists, you can create a subclass of LinkedList (or List, or any other List subclasses) or you can create a class with the list of lists as a field and add methods there to manipulate the list.
Well i've done this code and i've got it right
java.util.LinkedList mainlist = new java.util.LinkedList();
java.util.LinkedList sublist1 = new java.util.LinkedList();
sublist1.add(object1);
sublist1.add(object2);
sublist1.add(object3);
java.util.LinkedList sublist2=new java.util.LinkedList();
sublist2.add(1);
sublist2.add(2);
mainlist.add(sublist1);
mainlist.add(sublist2);
// To retrieve the sublist1 from mainlist...........
java.util.LinkedList temp = (java.util.LinkedList)mainlist.get(0);
Here variable mainlist is LinkedList of LinkedLists and variable temp contains the value the first list stored i.e sublist1..
You can even simplify access to the secondary lists, e.g. using
final List<List<String>> lists = new LinkedList<List<String>>() {
#Override
public List<String> get(final int index) {
while (index >= size()) {
add(new LinkedList<>());
}
return super.get(index);
}
};
This code automatically adds new LinkedLists to the outer list. With this code you can later easily add single values:
lists.get(2).add("Foo");
LinkedList<LinkedList<YourClass>> yourList = new LinkedList<LinkedList<YourClass>>();
As the declaration. To add another linked list (to the end by default) you would do
yourList.add(new LinkedList<YourClass>());
To add an element to lets say the second linked list in the series:
yourList.get(1).add(new YourClass());

Cannot create an array of LinkedLists in Java...?

I'm working on a sparse matrix class that needs to use an array of LinkedList to store the values of a matrix. Each element of the array (i.e. each LinkedList) represents a row of the matrix. And, each element in the LinkedList array represents a column and the stored value.
In my class, I have a declaration of the array as:
private LinkedList<IntegerNode>[] myMatrix;
And, in my constructor for the SparseMatrix, I try to define:
myMatrix = new LinkedList<IntegerNode>[numRows];
The error I end up getting is
Cannot create a generic array of LinkedList<IntegerNode>.
So, I have two issues with this:
What am I doing wrong, and
Why is the type acceptable in the declaration for the array if it can't be created?
IntegerNode is a class that I have created. And, all of my class files are packaged together.
For some reason you have to cast the type and make the declaration like this:
myMatrix = (LinkedList<IntegerNode>[]) new LinkedList<?>[numRows];
You can't use generic array creation. It's a flaw/ feature of java generics.
The ways without warnings are:
Using List of Lists instead of Array of Lists:
List< List<IntegerNode>> nodeLists = new LinkedList< List< IntegerNode >>();
Declaring the special class for Array of Lists:
class IntegerNodeList {
private final List< IntegerNode > nodes;
}
Aside from the syntax issues, it seems strange to me to use an array and a linked list to represent a matrix. To be able to access arbitrary cells of the matrix, you would probably want an actual array or at least an ArrayList to hold the rows, as LinkedList must traverse the whole list from the first element to any particular element, an O(n) operation, as opposed to the much quicker O(1) with ArrayList or an actual array.
Since you mentioned this matrix is sparse, though, perhaps a better way to store the data is as a map of maps, where a key in the first map represents a row index, and its value is a row map whose keys are a column index, with the value being your IntegerNode class. Thus:
private Map<Integer, Map<Integer, IntegerNode>> myMatrix = new HashMap<Integer, Map<Integer, IntegerNode>>();
// access a matrix cell:
int rowIdx = 100;
int colIdx = 30;
Map<Integer, IntegerNode> row = myMatrix.get(rowIdx); // if null, create and add to matrix
IntegerNode node = row.get(colIdx); // possibly null
If you need to be able to traverse the matrix row by row, you can make the row map type a TreeMap, and same for traversing the columns in index order, but if you don't need those cases, HashMap is quicker than TreeMap. Helper methods to get and set an arbitrary cell, handling unset null values, would be useful, of course.
class IntegerNodeList extends LinkedList<IntegerNode> {}
IntegerNodeList[] myMatrix = new IntegerNodeList[numRows];
myMatrix = (LinkedList<IntegerNode>[]) new LinkedList[numRows];
casting this way works but still leaves you with a nasty warning:
"Type safety: The expression of type List[] needs unchecked conversion.."
Declaring a special class for Array of Lists:
class IntegerNodeList { private final List< IntegerNode > nodes; }
is a clever idea to avoid the warning. maybe a little bit nicer is to use an interface for it:
public interface IntegerNodeList extends List<IntegerNode> {}
then
List<IntegerNode>[] myMatrix = new IntegerNodeList[numRows];
compiles without warnings.
doesn't look too bad, does it?
List<String>[] lst = new List[2];
lst[0] = new LinkedList<String>();
lst[1] = new LinkedList<String>();
No any warnings. NetBeans 6.9.1, jdk1.6.0_24
There is no generic array creation in Java 1.5 (or 1.6 as far as I can tell). See https://community.oracle.com/message/4829402.
If I do the following I get the error message in question
LinkedList<Node>[] matrix = new LinkedList<Node>[5];
But if I just remove the list type in the declaration it seems to have the desired functionality.
LinkedList<Node>[] matrix = new LinkedList[5];
Are these two declarations drastically different in a way of which I'm not aware?
EDIT
Ah, I think I've run into this issue now.
Iterating over the matrix and initializing the lists in a for-loop seems to work. Though it's not as ideal as some of the other solutions offered up.
for(int i=0; i < matrix.length; i++){
matrix[i] = new LinkedList<>();
}
You need an array of List, one alternative is to try:
private IntegerNode[] node_array = new IntegerNode[sizeOfYourChoice];
Then node_array[i] stores the head(first) node of a ArrayList<IntegerNode> or LinkedList<IntegerNode> (whatever your favourite list implementation).
Under this design, you lose the random access method list.get(index), but then you could still traverse the list starting with the head/fist node store in the type safe array.
This might be an acceptable design choice depending on your use case. For instance, I use this design to represent an adjacency list of graph, in most use cases, it requires traversing the adjacency list anyway for a given vertex instead of random access some vertex in the list.

Categories