Array List initialization displayed as boolean - java

I am trying to create an object with the following constructor
public PageBreak(String sheetName, ArrayList<Integer> rowPageBreaks, ArrayList<Integer> columnPageBreaks)
{
this.sheetName = sheetName;
this.rowPageBreaks = rowPageBreaks;
this.columnPageBreaks = columnPageBreaks;
}
In another method, I do the following call:
pageBreaks.add(new PageBreak(teamResultSheet.getName(),
new ArrayList<Integer>().add(teamResultSheet.getRowPageBreak()), null));
I then get the following error message: The constructor PageBreak (String, boolean, null) is undefined.
How can I create an ArrayList<Integer> on the spot?

You're going to want to populate your list before you attempt to use it. Here's what's happening:
You instantiate a new ArrayList<Integer>.
You immediately call add on that list.
The result of add is boolean (and consequently always returns true).
The boolean result is what is interpreted, and not the list.

While Makoto's answer is correct and explains why you are getting this error, and gives you sensible advice to create your ArrayList before you use it, you might want to know if there is any other way you can make your code more succinct.
Unlike more recent languages, such as Groovy or Kotlin, Java unfortunately does not have List literals as a language syntax. There have been some attempts to hack around this limitation; what you may have been attempting with your code is the double brace initialization idiom:
pageBreaks.add(new PageBreak(teamResultSheet.getName(),
new ArrayList<Integer>() {{add(teamResultSheet.getRowPageBreak());}},
null);
Although this may look cute, it does have its drawbacks, as described in the above link.
Do you really need to pass actual ArrayLists to your constructor? Why not make it take Lists, which will make it more flexible:
public PageBreak(String sheetName, List<Integer> rowPageBreaks, List<Integer> columnPageBreaks)
{
...
}
Then you have the freedom to pass it ArrayLists, as before, or any other kind of List:
pageBreaks.add(new PageBreak(teamResultSheet.getName(),
Arrays.asList(teamResultSheet.getRowPageBreak()),
null);
This looks more succinct and doesn't have the former's drawbacks; however, the list is fixed-size and immutable.
If you only want a single-element list, it's more efficient to use the following:
pageBreaks.add(new PageBreak(teamResultSheet.getName(),
Collections.singletonList(teamResultSheet.getRowPageBreak()),
null);

Related

Converting a generic List to an Array. Why do I need use clone?

I faced a problem yesterday, when I was writing my homework. I finished the homework, but I still don't really understand why my code works. I had to write a sort function that takes an varargs of any comparable generic object as an argument and return the argument. The problem was that I had to return an array of sorted objects. So I had to learn more about varargs lists and arrays.
The function was defined like this.
public <T extends Comparable<T>> T[] stableSort(T ... items)
and inside the function I made a list, which I would sort and do all the work on.
List<T> list = new ArrayList<T>(Arrays.asList(items));
and at the end of the function I was returning list toArray so that it matched the output type T[].
list.toArray(items.clone());
My question is since I already made the list from the varargs, why do I have to do items.clone() inside the toArray function. That seemed like doing two same things to me. I thought arrays.asList() would clone the values of array to list and I don't get why am I doing it again at the end of the code in toArray(). I know that this was the correct way to write it, because I finished the homework yesterday and found out this way from forums of the class, but I still don't understand why.
EDIT
The task required me to create a new array with sorted files and return it instead. Due to Type Erasure, it is not possible to instantiate an array of a generic type without a reference to a class that fits the generic. However, the varargs array has type T, so I should have cloned an array of a type which fits the generic constraints. Which I didn't know how to do in time. So I decided to use list to make my time easier till the deadline.
My question is since I already made the list from the varargs, why do I have to do items.clone()
You are right. Unfortunately, the compiler will be unable to determine the type of the array if you simply use the toArray() method. You should get a compilation error saying Cannot convert from Object[] to T[]. The call to item.clone() is required to assist the compiler in type-inference. An alternate approach would be to say return (T[])list.toArray
That said, I would not recommend either of the approaches. It doesn't really make sense to convert an array to a list and convert it back to an array in the first place. I don't see any significant take-aways that you would even understand from this code.
It seems to me there are a few questions here, that may have come together to create some confusion as to why what needs to be done.
I thought arrays.asList() would clone the values of array to list and I don't get why am I doing it again at the end of the code in toArray().
This is probably just the way it is typed, but it should be made clear that you don't clone the objects in the array, but only make a new List with the references to the objects in the array. The objects themselves will be the same ones in the array as in the List. I believe that is probably what you meant, but terminology can be tricky here.
I thought arrays.asList() would clone the values of array to list...
Not really. Using Arrays.asList(T[] items) will provide a view onto the array items that implements the java.util.List interface. This is a fixed-size list. You can't add to it. Changes to it, such as replacing an element or sorting in-place, will pass through to the underlying array. So if you do this
List<T> l = Arrays.asList(T[] items);
l.set(0, null);
... you've just set the element at index 0 of the actual array items to null.
The part of your code where you do this
List<T> list = new ArrayList<T>(Arrays.asList(items));
could be written as this:
List<T> temp = Arrays.asList(items);
List<T> list = new ArrayList<T>(temp);
The first line is the "view", the second line will effectively create a new java.util.ArrayList and fill it with the values of the view in the order they are returned in by their iterator (which is just the order in the array). So any changes to list that you make now don't change array items, but keep in mind that it's still just a list of references. items and list are referencing the same objects, just with their own order.
My question is since I already made the list from the varargs, why do I have to do items.clone() inside the toArray function.
There could be two reasons here. The first is as CKing said in his/her answer. Because of type erasure and the way arrays are implemented in Java (there are separate array types depending on whether it's an array of primitives or references) the JVM would not know what type of array to create if you just called toArray() on the list, which is why that method has a return type of Object[]. So in order to get an array of a specific type, you must provide an array to the method that can be used at run-time to determine the type from. This is a piece of the Java API where the fact that generics work via type-erasure, aren't retained at run-time and the particular way in which arrays work all come together to surprise the developer. A bit of abstraction is leaking there.
But there might be a second reason. If you go check the toArray(T[] a) method in the Java API, you'll notice this part:
If the list fits in the specified array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the specified array and the size of this list.
Suppose some code by another dev is using your stableSort method like this:
T[] items;
// items is created and filled...
T[] sortedItems = stableSort(items);
If you didn't do the clone, what would happen in your code would be this:
List<T> list = new ArrayList<T>(Arrays.asList(items));
// List is now a new ArrayList with the same elements as items
// Do some things with list, such as sorting
T[] result = list.toArray(items);
// Seeing how the list would fit in items, since it has the same number of elements,
// result IS in fact items
So now the caller of your code gets sortedItems back, but that array is the same array as the one he passed in, namely items. You see, varargs are nothing more than syntactic sugar for a method with an array argument, and are implemented as such. Perhaps the caller didn't expect the array he passed in as an argument to be changed, and might still need the array with the original order. Doing a clone first will avoid that and makes the effect of the method less surprising. Good documentation on your methods is crucial in situations like this.
It's possible that code testing your assignment's implementation wants a different array back, and it's an actual acquirement that your method adheres to that contract.
EDIT:
Actually, your code could be much simpler. You'll achieve the same with:
T[] copy = items.clone();
Arrays.sort(copy);
return copy;
But your assignment might have been to actually implement a sorting algorithm yourself, so this point may be moot.
You need to use this:
List<T> list = new ArrayList<T>(Arrays.asList(items));
when you want to do an inline declaration.
For example:
List<String> list = new ArrayList<String>(Arrays.asList("aaa", "bbb", "ccc"));
By the way, you didn't have to use return list.toArray(items.clone()); You could have used, for example, return list.toArray(Arrays.copyOf(items, 0));, where you are passing to list.toArray() an empty array that contains none of the arguments from items.
The whole point of passing an argument to the version of list.toArray() that takes an argument, is to provide an array object whose actual runtime class is the actual runtime class of the array object it wants to return. This could have been achieved with items.clone(), or with items itself (though that would cause list.toArray() to write the resulting elements into the original array pointed to by items which you may not want to happen), or with, as I showed above, an empty array that has the same runtime class.
By the way, the need to pass the argument to list.toArray() is not a generics type issue at all. Even if you had written this with pre-generics Java, you would have had to do the same thing. This is because the version of List::toArray() that took no arguments always returns an array object whose actual runtime class is Object[], as the List doesn't know at runtime what its component type is. To have it return an array object whose actual runtime class is something different, you had to give it an example array object of the right runtime class to follow. That's why pre-generics Java also had the version of List::toArray() that took one argument; even though in pre-generics, both methods were declared to return Object[], they are different as the actual runtime class returned is different.

Using Generic Input in a Functional Interface

I have an object that's designed to paginate a list and send a page of the results to a user. However, I want to be able to use Functions to pass behavior to this object. The behavior will be used by the object to dynamically generate the content of the lines using methods inside the objects in the list.
public class InteractiveJSONList<T> {
private final List<T> list;
private List<Function<T, Object>> formatArgFunctionSequence;
// ... Irrelevant code truncated.
// Constructor
public InteractiveJSONList(List<T> list) {
this.list = list;
}
}
I'm using generics to define the type of list, and likewise, the input type of the function. However, when attempting to define the Function as input to a method, NetBeans doesn't seem to recognize the input object as the correct type (it simply treats it as an instance of Object).
This code snippet resides inside an instance method in InteractiveJSONList. The method is designed to extract information from each item in the list and use the results as format arguments for String.format later on.
for (T item : itemsList) {
index++;
// Create a new JSON Message.
new JSONMessage(resultLinesBaseMessage)
.setFormatArguments(index, stringFunctionSequence.stream()
.map(o -> o.apply(item))
.collect(Collectors.toList())
.toArray(new Object[stringFunctionSequence.size()]))
}
I'm using Java 8's Stream methods to apply the function using each item in the list as input, then collecting the results and submitting them as an array for a varArgs method to use later in String.format.
Finally, this is an attempt to implement the mechanism.
List<String> data = ... //Using Strings at this point, but could be anything.
new InteractiveJSONList<String>(data)
.setStringBuildingSequence(input -> input.substring(0,5));
I'm only using String and its method substring as an example for how I want this mechanism to work. Certain objects in this list will be more complicated than Strings, and will have getters for variables inside the object. I want to define the behavior required to get variables from within the items in this list, and then use them to generate dynamic results. However, NetBeans won't recognize input as a member of String, and I subsequently am not able to use String's methods. input is only recognized as an object, even though InteractiveJSONList's generic type T is defined as a String when the object was created.
Am I setting up generics incorrectly? I might be led to think it's as simple as that, however, I tested this instead:
List<String> data = ... //Using Strings at this point, but could be anything.
Function<String, Object> testFunction = input -> new InteractiveText(input.substring(0, 8));
new InteractiveJSONList<String>(data)
.setStringBuildingSequence(testFunction);
And it compiled and ran correctly.
Any advice? Also, if this is a terrible practice for my desired purpose, please let me know. Thank you!

How do I create a array of LinkedLists and initialize all of them to an empty string?

I am trying to create an string array of linked lists in java. Then initialize everything inside the array to an empty string. Currently, they are all initialized with null because of the constructor. This causes a NullPointerException when I use an equals() method on the contents of the array. So I want to initialize them all to an empty string instead. How would I do this?
private LinkedList<String>[] table;
public Hashtable (int capacity, String hashFn) {
table = new LinkedList[capacity]
hashFn = hashFn;
}
I think you'll need to loop over and set each item to an empty string, like this...
private LinkedList<String>[] table;
public Hashtable (int capacity, String hashFn) {
table = new LinkedList[capacity];
String emptyString = "";
for (int i=0;i<capacity,i++){
table.add(emptyString);
}
}
I've never tried to do what you want to do, but I couldn't find any quick method to do it.
Of course, if you make it a String[] array rather than a LinkedList<String>[] array, you can simply call java.util.Arrays.fill(array,"");
If I understand your question correctly then in my opinion the better thing to do would be to use .equals() like following to avoid NPE, if you want to compare String from your LinkedList with any given String:
if("givenString".equals(strObjFromLinkedList)) {
//Do what you want
}
The direct answer is that the Collections class provides an nCopies method that you could use something like this:
List<String> l = new LinkedList<String>(Collections.nCopies(capacity, ""));
to avoid writing an explicit for loop. A for loop would be more efficient though.
But what I really want to say is that LinkedList is a BAD choice for implementing the primary hash table. Operations like get(int) and set(int, T) are O(N) for a linked list. You should be using an ArrayList<String>, or better still a String[]. These have O(1) operations for getting and setting an element. On top of that, they use significantly less memory.
In addition, you would be better of doing an explicit test for null than filling with empty strings. In reality, the JVM is likely to perform that null test behind the seens anyway. By doing it yourself, you 1) avoid the comparison with an empty string, and 2) save on the initialization time.
There is more than one way to fix an NPE problem ...

Any way to insert an anonymous array into a collection?

I've found plenty of entries here that say
someFunc(new int[]{1,2,3});
works for calling methods and being used in a for/each loop.
//======
How about for assignment into a collection?
I've tried this:
ArrayList<int[]> data = new ArrayList<int[]>();
data.add(new int[]{21, 19629});
and I get "identifier expected" and "illegal start of type".
Is there any thing I can do to make this work?
You've made a list of arrays. Was that really what you intended, or did you mean to ask about something like
ArrayList<Integer> data = new ArrayList<Integer>();
data.add(new int[]{1,2,3});
?
In any case, familiarize yourself with the Arrays and Collections classes--they contain a lot of utilities that come in handy in cases like this.
Easiest way to make a list from an array:
List<String> myList = Arrays.asList("Hello", "There", "Foo", "Bar);
Note the list is fixed size and backed by the input array so changes to the list actually write to the array. Adding or removing elements will likely result in an UnsupportedOperationException. If you intend to mess around with the array such as adding or removing elements you may need to copy the elements into another list
Check out guava, especially the google collections part.
Several of the collection classes define a static method of(...) which will the add all objects given to that collection.
Combined with static imports this allows very concise colleciton intializations on-the-fly.
e.g.
import static com.google.common.collect.ImmutableList.of;
...
someFunc(of("abc","def","ghi"));
will call someFunc with a list of strings.
In fact, this code works if you want to make a list of table of integers.
public static void main(String[] args) {
List<int[]> data = new ArrayList<int[]>();
data.add(new int[]{21, 19629})
for(int[] tab : data){//I loop each tab (only one here)
for(int i: tab){//I loop each values
System.out.println(i);
}
}
}
print
21
19629
This may be a limitation of the language syntax, or a bug in the spec that the identifier production is being found before the expression production in the parsing of the method call syntax. Don't mix anonymous array declaration inside the method call.
data.add(new int[]{21, 19629});
and I get "identifier expected" and
"illegal start of type".
The "identifier expected" in data.add(...) is probably caused by the compiler expecting an identifier and finding an expression that resolves to an anonymous instance. There are several places where that very syntax is acceptable, I'm not sure why data.add() needs an identifier.
The "illegal start of type" may be that the compiler thinks the expression is an anonymous type declaration but the syntax doesn't allow for that in a method call.
Try creating it using a variable, then pass the variable as the method arg.
int[] nums = new int[] {0,1,2};
data.add(nums);
In looking for more specifics of the ArrayList.add() grammar, I found this answer on a similar problem someone had learning ArrayList. JavaGlossary has a good set of Compile-time errors.

Casting an array of Objects into an array of my intended class

Just for review, can someone quickly explain what prevents this from working (on compile):
private HashSet data;
...
public DataObject[] getDataObjects( )
{
return (DataObject[]) data.toArray();
}
...and what makes this the way that DOES work:
public DataObject[] getDataObjects( )
{
return (DataObject[]) data.toArray( new DataObject[ Data.size() ] );
}
I'm not clear on the mechanism at work with casting (or whatever it is) that makes this so.
Because toArray() creates an array of Object, and you can't make Object[] into DataObject[] just by casting it. toArray(DataObject[]) creates an array of DataObject.
And yes, it is a shortcoming of the Collections class and the way Generics were shoehorned into Java. You'd expect that Collection<E>.toArray() could return an array of E, but it doesn't.
Interesting thing about the toArray(DataObject[]) call: you don't have to make the "a" array big enough, so you can call it with toArray(new DataObject[0]) if you like.
Calling it like toArray(new DateObject[0]) is actually better if you use .length later to get the array length. if the initial length was large and the same array object you passed was returned then you may face NullPointerExceptions later
I asked a question earlier about Java generics, and was pointed to this FAQ that was very helpful: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
To ensure type safety when casting an array like you intended (DataObject[] dataArray = (DataObject[]) objectArray;), the JVM would have to inspect every single object in the array, so it's not actually a simple operation like a type cast. I think that's why you have to pass the array instance, which the toArray() operation then fills.
After Java 8 with introduction of streams and Lambda you can do the following too:
For casting a normal Array of objects
Stream.of(dataArray).toArray(DataObject[]::new);
For casting a List
dataList.stream().toArray(DataObject[]::new);

Categories