How to unpack an array into different arguments on method call - java

I would like to know if it is possible to unpack an Object array into separate Object on method call which accepts vargs. This question is similar to this one.
I have a code like:
public class Test {
public static Object doWork(Object... objects){
System.out.println(objects.length);
return objects;
}
public static void main(String[] args){
Object res = doWork("one", "two");
res = doWork("three", res);
}
}
I would like to unpack the res object in the second call so it would receive an object array with length 3 instead of length 2 as now (where the second position is an Object array of length 2, having then all three arguments).
Is even that possible in Java?
More detailed:
By doing
Object res = doWork("one", "two");
res = doWork("three", res);
the second call gets called as:
doWork( Object[ "three", Object[ "one", "two" ] ] )
where i would like:
doWork(Object[ "one", "two", "three" ] )
I know this can be achieved by doing:
public static void main(String[] args){
res = doWork("one", "two");
List<Object> los = Arrays.asList(res);
los = new ArrayList<>(los); // Can't modify an underlying array
los.add("three");
res = doWork(los.toArray());
}
But I'm looking for something like the unpack Lua built in function or the Python way described in the previously mentioned SO question.
Both answers given by #chancea and #Cyrille-ka are good and also solve the problem. One of the facts that might be a good idea to take into account is if the signature of the method can be modified.
#cyrille-ka answer respects the function's signature, whereas #chancea does not. However I think in most cases one can just write asimple wrapper function to another one, so that shouldn't be a problem.
On the other hand #chancea's way might be easier to use while programing (there no possible mistake of forgetting to call the unpack function).

Well, there is no syntactic sugar à la Python or Lua for that in Java, but you can create your own unpack method:
#SafeVarargs
public static <E> Object[] unpack(E... objects) {
List<Object> list = new ArrayList<Object>();
for (Object object : objects) {
if (object instanceof Object[]) {
list.addAll(Arrays.asList((Object[]) object));
}
else{
list.add(object);
}
}
return list.toArray(new Object[list.size()]);
}
This will returns an array containing all the input elements, unpacked.
Then you can call it in your main method:
res = doWork(unpack("three", res));
The reason I make this method generic is that you can call it with, for example, a String[] array, without generating a warning. For some reason, the Java compiler thinks that this method has a chance of "polluting the heap", but it is not the case, that's why I added a #SafeVarargs annotation.

This does not implement the Unpack solution, instead it goes about it by making an overload method as I said in my comment. I do not know if this at all what you wanted, but I got it to work and I felt like I would post this for reference.
public class Test {
public static Object doWork(Object... objects){
System.out.println(objects.length);
return objects;
}
// this is the method that will concatenate the arrays for you
public static Object doWork(Object[] objects1, Object... objects2){
System.out.println(objects1.length + "+" + objects2.length);
Object[] retval = new Object[objects1.length+objects2.length];
System.arraycopy(objects1, 0, retval, 0, objects1.length);
System.arraycopy(objects2, 0, retval, objects1.length, objects2.length);
return retval;
}
public static void main(String[] args){
Object res = doWork("one", "two");
res = doWork((Object[])res, "three");
Object[] res2 = (Object[])res; // = {one, two, three}
}
}

Try to return as an array of Object instead. Then concatenate "three" at the end of the returned array.

Related

Method to flatten generic array

I've been trying to write a method that will flatten a generic array if it is nested.
private static <T> List<T> flatten(T[] in) {
List<T> result = new ArrayList<>();
for (T e : in) {
if (e.getClass().isArray()) {
result.addAll(Arrays.asList(e)); ## Issue is here.
} else {
result.add(e);
}
}
return result;
}
This code does not cause any errors but also does not work. When e is not an array, things work as expected... a list is populated with the elements of in and returned.
However when e.getClass().isArray() == true, the elements of e are not added. Rather the original array is added so I end up with a list of arrays.
My use case here is that I have a method that is being passed generics T[] someArray
public <T> void doSomeStuff(T[] someArray) {
Set<T> unique = Sets.newHashSet(someArray)
... do some stuff with the unique values ...
}
The input someArray may either be nested or not (i.e. T itself may be an array, resulting in T[][]). I want to determine the unique elements contained in the input, whether or not it is nested. Passing the input someArray to a set only works if it's not nested, hence I'm trying to flatten.
So my question is, how can I do this and why is my method above not working? Thanks in advance for the edcuation.
Your code can't work. The generics just don't line up.
Let's say you have an array that is a combination of strings and arrays of strings. That cannot possibly be a T[] unless T is object, which isn't what you want (as that would mean you get a List<Object>. After all, If T is String, then your input array, which is defined as T[] in, is a String[] in, which cannot contain string arrays. After all, a String[] is not a subtype of String, for obvious reasons.
It is impossible to describe in terms of generics the concept of 'an array of Strings, or an array of arrays of Strings, or an array of arrays of arrays of Strings, and so forth'. So, generics have no place here. If you want that, all you can 'type' is 'an array whose component type is unknown and hybrid anyway', which is Object[] in java (this is co/contra-variance wise broken, but this is just part of the java spec: Variance on arrays is incorrect, known problem and not fixable).
This gets you a secondary issue: Generics are erased, and in that model you don't have an actual type to work with. In fact, because it is impossible to use generics to tell the compiler to do some type checking on the input array, there is nothing the compiler can do for you, so any type checking you want (and you clearly want that, you don't want to return a List of who knows what this is), will have to be done at runtime.
Unfortunately, it is impossible to do that, too - you can't check if at runtime if some object is, say, a Map<String, Integer>.
So, what you want is impossible.
It becomes possible if you're okay with this method being only able to do the job for reified types. That is, types that don't contain any <> themselves. So, if you want to take 'an array that contains a combination of "Map of string to integer" and "arrays of Maps of string to integer"', this method will not be able to do that and it is in fact completely impossible to do such a thing in java. But if you're okay with, say, "An array containing a combination of strings and arrays of strings" and want to turn that into a flattened-out list of strings, okay, that's possible.
It's complicated, though:
public <T> List<T> flattenArray(Class<T> type, Object[] in) {
if (type.isArray()) throw new IllegalArgumentException();
var out = new ArrayList<T>();
flattenArray0(type, in, out);
return out;
}
private <T> void flattenArray0(Class<T> type, Object[] in, List<T> out) {
for (Object a : in) {
if (a == null) {
out.add(null);
} else if (a.getClass().isArray()) {
flattenArray0(type, (Object[]) a, out);
} else {
out.add(type.cast(a));
}
}
}
In action:
Object[] test = new Object[3];
test[0] = "Hello";
test[1] = new String[] {"Foo", "Bar"};
Object[] threeDeep = new Object[2];
test[2] = threeDeep;
threeDeep[0] = "Goodbye";
threeDeep[1] = new String[] {"Baz"};
List<String> result = flattenArray(String.class, test);
System.out.println(result);
should print: ["Hello", "Foo", "Bar", "Goodbye", "Baz"].

How to pass array of string and a string as a vararg?

I want to pass an array and a single object to method which has varargs.
However, the most obvious solution doesn't seem to work:
public static final String[] ARRAY_ARGS = {"first argument", "second argument"};
public static String additionalArgument = "additional argument";
public static void foo(String... args) {
// ...
}
public static void main(String[] args) {
foo(ARRAY_ARGS,additionalArgument); // error! won't compile
}
How can I fix this?
A variable argument is equivalent to an array. Hence, the compiler does not accept an array and a string. One solution is to create an array from the original with the additional string added to it:
List<String> originalList = Arrays.asList(ARRAY_ARGS);
List<String> list = new ArrayList<String>(originalList);
Collections.copy(list, originalList);
list.add(additionalArgument);
foo(list.toArray(new String[list.size()]));
The Collections.copy is needed because adding to the list returned by Arrays.asList throws a java.lang.UnsupportedOperationException as the latter returns a list that extends AbstractList which does not support adding elements.
Another solution is to create a new array and individually add the elements:
String[] arr = new String[3];
arr[0] = ARRAY_ARGS[0];
arr[1] = ARRAY_ARGS[1];
arr[2] = additionalArgument;
foo(arr);
Or you can simply call foo with the individual parameters:
foo(ARRAY_ARGS[0], ARRAY_ARGS[1], additionalArgument);
You can do this, if the first argument to your method is the single string, followed by the varargs:
public static void foo(String a, String... args) {
System.out.println("a = " + a);
System.out.println("args = " + Arrays.toString(args));
}
Then calling foo("a", "b", "c") prints
a = a
args = [b, c]
The argument String ... args is shorthand for String[]. Therefore you get an error when you are calling foo(args,additionalargument) since the method declaration of foo is foo(String[] str) instead of foo(String[],String).
If you must distinguish between the array and the single string, concatenate the array values in a format you can "recognize", pass the "array-string" and the single string as parameters to the main method, and then parse the "array-string" in the relevant (foo) method.
A Java 8 approach to solve this problem is the following oneliner:
foo(Stream.concat(Arrays.stream(ARRAY_ARGS), Stream.of(additionalArgument)).toArray(String[]::new));
This creates a new String[] that can be passed to foo. Furthermore, foo must be made static, otherwise the code will not compile.
The only parameters that you can pass to the foo-method is either a String array or a bunch of individual Strings.
Simple yet elegant solution using Apache Commons Lang library:
foo(ArrayUtils.addAll(ARRAY_ARGS,additionalArgument));

Removing Null elements in an array

I'm reading from an XML file to populate some data structures and I run into this sort of problem when I inspect my final structure:
arrayName
[0] = null
[1] = input
[2] = null
[3] = input
etc
input is what I want, and the type is my own class.
I'm used to C# so I'd use LINQ to get rid of them normally, idea for doing something like this in Java?
I'm going to look at what's wrong with the code that's making it happen but for now I need a quick solution.
Ideas?
EDIT:
I found the issue, I create an array of size doc.getChildNodes().getLength(), and when I'm setting elements in the array (while looping through), I check if
getNodeType() == Node.ELEMENT_NODE)
And it doesn't work half the time. Problem is, I initialise the array based on the size, so half the array gets filled.
Ideas?
Arrays are immutable in Java (not their contents, the array itself). There is no such thing as a dynamic sized array or changing of the length. So you would iterate, count, create a new array, copy... or use an appropriate datastructure in the first place, maybe even one that capsules the creation of new arrays and offers manipulation methods like ArrayList.
Something like LINQ does not exist yet, you need some explicit object that capsules the manipulation or filtering.
If you're not having excessive amounts of data, you could just blend it through a few collections.
public class Main {
public static void main(String[] args) {
String[] arr = {"haha", "hoho", null, "hihi", null };
System.out.println(Arrays.toString(arr));
Set<String> set = new HashSet<>(Arrays.asList(arr));
set.remove(null);
arr = new String[set.size()];
arr = set.toArray(arr);
System.out.println(Arrays.toString(arr));
}
}
Output:
[haha, hoho, null, hihi, null]
[hoho, haha, hihi]
Keep in mind that this first allocates the original array, then creates a list from that array, then creates a hashset from that list and eventually puts everything back in the original array.
If you're having a very large amount of data it might constipate you a little but otherwise it's very easy to read and really just uses built-in features to reach what you want.
Unfortunately, there's nothing like LINQ in Java. The best thing would be probably checking for null beforehand, and only inserting if the element is not null, eg. (assuming your class name is InputClass:
InputClass c = parseFromXml(...);
if (c != null) {
myList.add(c);
}
Alternatively, you can remove nulls by iterating and copying (I use a list as an intermediary artifact):
InputClass[] removeNulls(InputClass[] original) {
List<InputClass> nonNulls = new ArrayList<InputClass>();
for (InputClass i : original) {
if (i != null) {
nonNulls.add(i);
}
}
return nonNulls.toArray(new InputClass[0]);
}
You can also use generics and make your method <T> removeNulls(T[] original) instead.
It sounds like you want a method to give you a new Array with the null values removed; perhaps something like this -
public static <T> T[] removeNulls(T[] in) {
if (in == null) {
return in;
}
Class<?> cls = null;
List<T> al = new ArrayList<T>();
for (T t : in) {
if (t != null) {
if (cls == null) {
cls = t.getClass();
}
al.add(t);
}
}
#SuppressWarnings("unchecked")
T[] out = (T[]) Array.newInstance(cls, al.size());
return al.toArray(out);
}

compare two Lists of objects and return a list which has common objects in two

I have two lists -
ArrayList<MyObject> list1 -> [obj1, obj2, obj3, obj4, obj5, obj6]
ArrayList<MyObject> list2 -> [obj1, obj6, obj7, obj8]
Is there any utility method already existing in java api which can give a list of only common records of two?
expected list ->
[obj1, obj6]
MyObject looks like this -
class MyObject {
public Integer number;
public String name;
public Integer parent;
public String parentName;
}
I only need to use number and name to do the comparison.
i am aware of approach using retainAll or removeAll to get the desired list. The problem however is, I cannot override equals method of MyObject as it is used for some different purpose. And the retainAll or removeAll method doesn't seems to accept Comparator object.
I know another solution is to iterate through the lists and find the common ones. I am looking if there is already some method which do it.
Thanks.
I don't know of any cleaner way than what you already suggest.
However, if you need equality but can't use equals (I'm not going to ask ;) you could merge the lists and sort them, and then compare manually each element to the next, keeping only the duplicate ones.
If you want to get the same value from two list, I give you a string example:
/**
* #param args
* #throws IOException
*/
public static void main(String[] args) throws IOException {
// create a script engine manager
List<String> list_1 = new ArrayList<String>();
list_1.add("a");
list_1.add("b");
List<String> list_2 = new ArrayList<String>();
list_2.add("a");
list_2.add("b");
list_2.add("c");
System.out.println(getSameVaue(list_2,list_1));
}
public static List<String> getSameVaue(List<String> list1,
List<String> list2) {
List<String> result = new ArrayList<String>();
if (list1.size() > list2.size()) {
for (String s : list1) {
if (list2.contains(s)) {
result.add(s);
}
}
} else {
for (String s : list2) {
if (list1.contains(s)) {
result.add(s);
}
}
}
return result;
}
But if you want to use your Object, please do not forget implement equals method.

How to convert a List of String arrays to a List of objects

I want to convert a List to a List so that each object on my new list includes the first element of each String[].
Do you know if this is possible to do in java?
for example:
public List<String[]> readFile(){
String[]array1={"A","1.3","2.4","2.3"};
String[]array2={"B","1.43","3.4","2.8"};
String[]array3={"C","5.23","2.45","2.9"};
List<String[]>ReadFile= new ArrayList<String[]>();
ReadFile.add(array1);
ReadFile.add(array2);
ReadFile.add(array3);
return ReadFile;
}
Now I want a method which will take the List ReadFile from above to somehow split the arrays of strings into an ID which will be the first element "A", "B", "C" and another part which would be the string array of numbers which I will put through another method to convert numbers from String to type Double. I have already got the method to convert to double but I need to be able to keep track of the ID field because the ID field will be used to identify the array of numbers.
A friend suggested that I create an Object where each objects has one part as a String ID and the other part as an array. That is the part which I do not know how to do.
Can anybody help please?
below is the method declaration which I believe I should have so the return type will be List where each array has been converted to an Object with two parts.
public List<Object> CreateObject(List<String[]>ReadFile){
}
Thanks,
Jetnori.
Hi all, Thank you for taking your time to help.
I can see the benefit of using HashTables. I am as of now trying to implement it. I know i might be sidetracking a little but just to explain what I am trying to do:
In my project I have CSV file with data about gene expression levels. The method that I use from OpenCSV to read the file returns a List(String[]) where each String[] is one row in the file. The first element of each row is variable name (recA, ybjE etc). The rest of the row will be numbers data related to that variable. I want to calculate Pearson's correlation between each of the number arrays. The method which I have got implemented already does that for me but the problem that I have now is that I had to remove the string values from my arrays before I could convert to double by iterating over the array. After I have managed to calculate the correlation between each array of doubles by still keeping the ID linked to the row, I want to be able to draw an undirected node graph between the genes that have a correlation higher than a threshold which I will set (for example correlation higher than 0.80). I don't know if i am biting more than i can chew but I have 30 days to do it and I believe that with the help of people like you guys I will get through it.
Sorry for going on for a bit.
thanks,
Jetnori.
I agree with the answer Alb provided, however this is what your friend has suggested, first you need a class to represent the data. I have included a constructor that parses the data and one that accepts already parsed data, depending on how you like to think of things.
public class NumberList {
private double[] numbers;
private String key;
public NumberList(Strig key, double[] numbers){
this.ley = key;
this.numbers = numbers;
}
public NumberList(String[] inputList) {
key = inputList[0];
numbers = new double[inputList.length-1];
for(int i=1;i<inputList.length;i++){
numers[i-1] = Double.parseDouble(inputList[i]);
}
}
public String getKey() {
return key;
}
public double[] getNumbers() {
return numbers;
}
}
Then you need your function to generate the list:
public List<NumberList> CreateObject(List<String[]> ReadFile){
ArrayList<NumberList> returnList = new ArrayList<NumberList>(ReadFile.size());
for (String[] input : ReadFile) {
returnList.add(new NumberList(input));
}
return returnList;
}
Note this uses the constructor that parses the data, if you use the other constructor then the "CreateObject" function would need to include the parsing logic.
Finally on a side note the standard convention in java is that the only thing that is capitalized are class names and final static fields (which appear in all caps sepearted by underscores), so conventionally the method signature would be:
public List<NumberList> createObject(List<String[]> readFile){
...
}
Sounds like you need a Map instead of a List, it lets you index things by a key (in your case ID).
Map<String, String[]> map = new Hashmap<String, String[]>();
for( String[] array : ReadFile ){
map.put( array[0], array );
}
then to get the array of values for 'A' you would do:
String[] values = map.get( "a" );
If you want the values to be doubles instead of strings you'll want to change the array before putting it (the map.put call) I'd advise using a list or other collections instead of an array also. You also will probably also want to remove the ID part from these values, which my code does not do.
public class Split_ListwithIDs {
Hashtable<String, String[]> table = new Hashtable<String, String[]>();
Splitter spl ;
public Split_ListwithIDs(Splitter split){
spl = split;
}
private void addEntry(String key , String[] vals){
table.put(key, vals);
}
public void parseList(List<String[]> list){
for(String[] entry : list){
String[] temp = new String[entry.length - 1];
System.arraycopy(entry, 1, temp, 0,entry.length - 1);
addEntry(entry[0], spl.GetStringArrayOfNumbers(temp));
}
}
class SplittingHelper implements Splitter{
#Override
public String[] GetStringArrayOfNumbers(String[] arr) {
String[] strArray = null ;
// implementation here
return arr;
}
}
interface Splitter {
String[] GetStringArrayOfNumbers(String[] arr);
}
}
You will have to use a Hashtable instead of a list of objects.( I am assuming that you will need to search through the list for a given entry using the First alphabet as key - This will be very laborious if you want to use a List ).
In the method SplittingHelper , provide your custom logic to parse the string of numbers and return another string[] of numbers.
I don't understand your goal, but for 'an object with 2 parts' you might consider storing them in a Hashtable: http://download.oracle.com/javase/6/docs/api/java/util/Hashtable.html

Categories

Resources