passing Data in method that using generic ? it's that possible? - java

I'm trying to understand how to use a method that uses generic. And I want to create a generic method that can passing data to it. for example I want to create an array and add it with a variable in that Method. Byetheway I'm using Java
this is the code that i was Trying:
public class test {
static int d;
public static <E> void AdditionArray (E[] arr, int var) {
for (E element : arr) {
d = element + var; //this is the big problem, I have no idea whether this is possible or not
System.out.println(d);
}
}
public static void main(String[] args) {
Integer [] arr1 = {1,4,5,7};
AdditionArray(arr1, 2);
}
}
so one line of my codes definitely was wrong, but how can i do that ?
a Method that using Generic and passing data to it ?

If you want use generic to add array element consider extending Number type
for instance:
public class Main {
static int d;
public static <E extends Number> void AdditionArray(E[] arr, int var) {
for (E element : arr) {
d = element.intValue() + var; //this is the big problem, I have no idea whether this is possible or not
System.out.println(d);
}
}
static String z;
public static <E extends CharSequence> void AdditionArray2(E[] arr, int var) {
for (E element : arr) {
z += element.toString() + var; //this is the big problem, I have no idea whether this is possible or not
System.out.println(z);
}
}
public static void main(String[] args) {
Integer[] arr1 = {1, 4, 5, 7};
AdditionArray(arr1, 2);
Double[] arr2 = {1.0, 4.0, 5.0, 7.0};
AdditionArray(arr2, 2);
String[] arr3 = {"a", "b", "c", "d"};
AdditionArray2(arr3, 2);
}
}

If you are familiar with what a Lambda is, you can do this very succinctly in Java 8+ using lambdas.
Here's an example
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
public class GenericAddition {
public static <E> List<E> map(List<E> elements, Function<E, E> mapper) {
List<E> result = new ArrayList<>();
for (E element : elements) {
result.add(mapper.apply(element));
}
return result;
}
public static void main(String...args) {
List<Integer> addedThree = map(Arrays.asList(5, 3, 4), (elem) -> elem + 3);
System.out.println(addedThree);
List<String> concatted = map(Arrays.asList("good", "guten", "baz"), (elem) -> elem + " day");
System.out.println(concatted);
}
}
This will print out:
[8, 6, 7]
[good day, guten day, baz day]
One catch is you have to use a List type because using E[] could be an array of primitives but your mapper function needs to take in objects (since type parameters must be of object types). You could easily code around this restriction by adding a wrapper function that converts an array to a list, Java will be smart and perform auto-boxing for you in that case.
I personally think this is very readable and also very flexible since you can define any kind of logic for performing the mapping i.e. if I wanted to do something like add 5 if its odd, or *2 if its even, I can define my own lambda function for doing this. That way, I can get more complex behavior without having to write separate methods for each different type that I want.
If you are using an older Java version, you could do the same thing with anonymous inner classes that implement a particular interface.

Related

Java PriorityQueue Comparator to insert a 2D array under certain conditions

I have a 2D array arr= [[1,4],[4,4],[2,2],[3,4],[1,1]]
I want to put it in a PriorityQueue in such a way that it is stored in the following order:
[[1,1],[2,2],[1,4],[3,4],[4,4]]
i.e
If the first element is same for two arrays then the one with lower value of second element should appear first
If the second element is same for two arrays then the one with lower value of first element should appear first
Else the one with lower value of second element should appear first
I tried the following code :
import java.io.*;
import java.util.*;
class Check2
{
public static void main(String[] args) {
int events[][]={{1,4},{4,4},{2,2},{3,4},{1,1}};
PriorityQueue<int[][]> min_heap= new PriorityQueue<int[][]>(events ,new Comparator<Integer>()
{
#Override
public int compare(int a[][], int b[][])
{
int res=-1;
if(a[0][1]==b[0][1])
{
if(a[0][0]>b[0][0])
res=-1;
else
res=1;
}
else
{
if(a[0][1]>b[0][1])
res=-1;
else
res=1;
}
}
}
);
}
}
I'm getting the following error :
Check2.java:8: error: <anonymous Check2$1> is not abstract and does not override abstract method compare(Integer,Integer) in Comparator
{
^
Check2.java:9: error: method does not override or implement a method from a supertype
#Override
^
Check2.java:7: error: incompatible types: int[][] cannot be converted to int
PriorityQueue<int[][]> min_heap= new PriorityQueue<int[][]>(events ,new Comparator<Integer>()
^
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
3 errors
I understand that the compare function wont work for 2d arrays here, but how do I achieve the result.
I tried implementing other techniques by checking everywhere but those didn't worked out. Please help.
In line
PriorityQueue<int[][]> min_heap= new PriorityQueue<int[][]>(events ,new Comparator<Integer>()
you provide collection as first argument, but PriorityQueue doesn't have such constructor. You can't provide comparator and collection in one constructor.
And still you don't need to store int[][] in PriorityQueue to solve this, store int[] instead.
public static void main(String[] args) {
int[][] events ={{1,4},{4,4},{2,2},{3,4},{1,1}};
PriorityQueue<int[]> min_heap = new PriorityQueue<>((x, y) -> {
if(x[0] != y[0]) {
return Integer.compare(x[0], y[0]);
}
if(x[1] != y[1]) {
return Integer.compare(x[1], y[1]);
}
return 0;
});
min_heap.addAll(Arrays.asList(events));
while(min_heap.peek() != null) {
System.out.println(Arrays.toString(min_heap.poll()));
}
}
Output:
[1, 1]
[1, 4]
[2, 2]
[3, 4]
[4, 4]
PriorityQueue<int[]> minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
for (int[] a: arr) {
minHeap.offer(a);
}

How do I sort two arrays in relation to each other?

I prototype in python and I'm used the zip function for this, I'm not sure how to do this in Java. Basically I have two lists (one is names and one is data) and want them sorted in relation to each other. My program only processes a list (data, in this case) but I use the names as a reference to what data I'm processing and I want to try to experiment with processing my data in a different order. Here's an example of the structure (in reality my data is not given to me stored but I would do either a basic sort or a reverse sort on it, nothing fancy).
String[] names = new String[]{"Monkey1", "Dog2", "Horse3", "Cow4", "Spider5"};
int[] data = new int[]{1, 2, 3, 4, 5};
so the inverse would be
name = Spider5, Cow4, Horse3, Dog2, Monkey1
data = 5, 4, 3, 2, 1
I found this question: Is there an accepted Java equivalent to Python's zip(), but I would rather (if possible and for the faint of heart) do this using libraries I already have (Java commons, apache commons, etc). If there's no other way then I'll give functional java a shot. Any suggestions?
If you really don't want to redo your data-structures to combine the infos, you can use a Multimap to do it.
This example utilizes the excellent Google-Guava Library, which you should be using anyway :) https://code.google.com/p/guava-libraries/
String[] names = new String[] {"Monkey1", "Dog2", "Horse3", "Cow4", "Spider5"};
int[] data = new int[] {1,2,3,4,5};
/* guava, throws an IllegalStateException if your array aren't of the same length */
Preconditions.checkState(names.length == data.length, "data and names must be of equal length");
/* put your values in a MultiMap */
Multimap<String, Integer> multiMap = LinkedListMultimap.create();
for (int i=0; i<names.length; i++) {
mmap.put(names[i], data[i]);
}
/* our output, 'newArrayList()' is just a guava convenience function */
List<String> sortedNames = Lists.newArrayList();
List<Integer> sortedData = Lists.newArrayList();
/* cycle through a sorted copy of the MultiMap's keys... */
for (String name : Ordering.natural().sortedCopy(mmap.keys())) {
/* ...and add all of the associated values to the lists */
for (Integer value : mmap.get(name)) {
sortedNames.add(name);
sortedData.add(value);
}
}
Here's complete code:
StringIntTuple.java:
public class StringIntTuple{
public final int intValue;
public final String stringValue;
public StringIntTuple(int intValue, String stringValue){
this.intValue = intValue;
this.stringValue = stringValue;
}
public String toString(){
return "(" + this.intValue + ", " + this.stringValue + ")";
}
}
StringIntTupleStringComparator.java:
import java.util.Comparator;
public class StringIntTupleStringComparator implements
Comparator<StringIntTuple> {
#Override
public int compare(StringIntTuple a, StringIntTuple b) {
// TODO Auto-generated method stub
return a.stringValue.compareTo(b.stringValue);
}
}
StringIntTupleIntComparator.java:
import java.util.Comparator;
public class StringIntTupleIntComparator implements Comparator<StringIntTuple> {
#Override
public int compare(StringIntTuple a,
StringIntTuple b) {
return ((Integer)a.intValue).compareTo((Integer)b.intValue);
}
}
Driver.java:
import java.util.ArrayList;
import java.util.Collections;
public class Driver {
/**
* #param args
*/
public static String[] names = new String[] {"Monkey1", "Dog2", "Horse3", "Cow4", "Spider5"};
public static int[] data = new int[] {1,2,3,4,5};
public static void main(String[] args) {
ArrayList<StringIntTuple> list = new ArrayList<StringIntTuple>();
for(int i =0; i<names.length; i++){
list.add(new StringIntTuple(data[i],names[i]));
}
Collections.sort(list, new StringIntTupleIntComparator());
System.out.println(list.toString());
Collections.sort(list, new StringIntTupleStringComparator());
System.out.println(list.toString());
}
}
Output (sorted first by int field, then by String field):
[(1, Monkey1), (2, Dog2), (3, Horse3), (4, Cow4), (5, Spider5)]
[(4, Cow4), (2, Dog2), (3, Horse3), (1, Monkey1), (5, Spider5)]
EDIT 1 (extra info):
If you want to make this work for any Tuple, i.e. which doesn't constrain the field types to int, String, you can simply do the same operation with generics, i.e.:
public class Tuple<A,B>{
public Tuple(A aValue, B bValue){
this.aValue = aValue;
this.bValue = bValue;
}
public final A aValue;
public final B bValue;
}
Then, just tweak the Comparators accordingly, and you have a generic solution.
EDIT 2(After lunch): Here it is.
public class TupleAComparator<A extends Comparable<A>,B extends Comparable<B>> implements Comparator<Tuple<A,B>> {
#Override
public int compare(Tuple<A, B> t1, Tuple<A, B> t2) {
return t1.aValue.compareTo(t2.aValue);
}
}
EDIT 3: Code supplement as answer to Comment #1 (augmenting comment #2)
TupleArrayList.java:
import java.util.ArrayList;
import java.util.List;
public class TupleArrayList<A,B> extends ArrayList<Tuple<A,B>> {
/**
* An ArrayList for tuples that can generate a List of tuples' elements from a specific position within each tuple
*/
private static final long serialVersionUID = -6931669375802967253L;
public List<A> GetAValues(){
ArrayList<A> aArr = new ArrayList<A>(this.size());
for(Tuple<A,B> tuple : this){
aArr.add(tuple.aValue);
}
return aArr;
}
public List<B> GetBValues(){
ArrayList<B> bArr = new ArrayList<B>(this.size());
for(Tuple<A,B> tuple : this){
bArr.add(tuple.bValue);
}
return bArr;
}
}
So the obvious answer here is to wrap the name and data values in a class. Then maintain a List of that class. The class should implement equals, hashCode and Comparable which then then allow sorting the list using Collections.sort.
Maintain related data in two different lists is anti-OOP.
Something like this.
class MyWrapper implements Comparable<MyWrapper>{
private String name;
private int data;
}
List<MyWrapper> listToBeSorted;
The "right" way to do this in Java is to create a combined object that holds the corresponding elements, and to sort that.
Example:
class NameAndData {
private final String name;
private final int data;
}
List<NameAndData> toBeSorted;
and then you create a list of the combined elements and sort that. Basically, you're writing your own specific Pair class. (I, and many Java developers, think that adding a Pair class to Java would just lead to more obfuscated code -- a LatLong class, for example, is much less ambiguous about what it means than a Pair<Double, Double>.)
In some cases it doesn't make much sense to create a new class just to do concurrent sorting.
Here, is a function that can be used to sort an arbitrary number of Lists with arbitrary types based on a key that implement Comparable (Ideone Example here).
Usage
Here is an example of how you can use the function to sort multiple lists of arbitrary types:
// Can be any type that implements Comparable, Dupes are allowed
List<Integer> key = Arrays.asList(4, 3, 1, 2, 1);
// List Types do not need to be the same
List<String> list1 = Arrays.asList("Four", "Three", "One", "Two", "One");
List<Character> list2 = Arrays.asList('d', 'c', 'a', 'b', 'a');
// Sorts key, list1, list2
// Remove second key if you don't want to sort key.
multiSort(key, key, list1, list2);
Output:
key: [1, 1, 2, 3, 4]
list1: [One, One, Two, Three, Four]
list2: [a, a, b, c, d]
Code
An Ideone Example can be found here which includes validation of parameters and a test case.
public static <T extends Comparable<T>> void multiSort(
final List<T> key, List<?>... lists){
// Create a List of indices
List<Integer> indices = new ArrayList<Integer>();
for(int i = 0; i < key.size(); i++) {
indices.add(i);
}
// Sort the indices list based on the key
Collections.sort(indices, new Comparator<Integer>() {
#Override public int compare(Integer i, Integer j) {
return key.get(i).compareTo(key.get(j));
}
});
// Create a mapping that allows sorting of the List by N swaps.
// Only swaps can be used since we do not know the type of the lists
Map<Integer,Integer> swapMap = new HashMap<Integer, Integer>(indices.size());
List<Integer> swapFrom = new ArrayList<Integer>(indices.size()),
swapTo = new ArrayList<Integer>(indices.size());
for (int i = 0; i < key.size(); i++) {
int k = indices.get(i);
while (i != k && swapMap.containsKey(k)) {
k = swapMap.get(k);
}
swapFrom.add(i);
swapTo.add(k);
swapMap.put(i, k);
}
// use the swap order to sort each list by swapping elements
for (List<?> list : lists)
for (int i = 0; i < list.size(); i++)
Collections.swap(list, swapFrom.get(i), swapTo.get(i));
}
You could use a ConcurrentSkipListMap which can provide forward and reverse iterators over the keys. If you are looking for arbitrary re-orderings besides a fixed forward and reverse ordering, you'll have to go to something else. Or you can always keep a simple HashMap or whatever to maintain parallel item associations, and then construct a SortedMap (Treemap or ConcurrentSkipListMap) as needed by providing an appropriate Comparator.
The disadvantage of this approach is that the associations between keys/values are much more transient, and can be more easily and accidentally broken by updates to the map. All of the other answers that create Tuples, Pairs, or other explicit 1-1 relationships address that better. Of course, if you intend for the associations to be more fluid, then just using a map adds a bit of an advantage.
Assuming that the lengths of these two arrays are the same, you can create a list of map entries containing pairs of elements from these arrays, and sort this list in reverse order by key as follows:
String[] names = new String[]{"Monkey1", "Dog2", "Horse3", "Cow4", "Spider5"};
int[] data = new int[]{1, 2, 3, 4, 5};
List<Map.Entry<Integer, String>> entryList = IntStream
.range(0, names.length)
.mapToObj(i -> Map.entry(data[i], names[i]))
.sorted(Map.Entry.<Integer, String>comparingByKey().reversed())
.collect(Collectors.toList());
System.out.println(entryList);
// [5=Spider5, 4=Cow4, 3=Horse3, 2=Dog2, 1=Monkey1]
If you want to replace the contents of the arrays:
IntStream.range(0, entryList.size()).forEach(i -> {
data[i] = entryList.get(i).getKey();
names[i] = entryList.get(i).getValue();
});
System.out.println(Arrays.toString(data));
// [5, 4, 3, 2, 1]
System.out.println(Arrays.toString(names));
// [Spider5, Cow4, Horse3, Dog2, Monkey1]
See also: Sorting two parallel arrays

Java oneliner for list cleanup

Is there a construct in java that does something like this(here implemented in python):
[] = [item for item in oldList if item.getInt() > 5]
Today I'm using something like:
ItemType newList = new ArrayList();
for( ItemType item : oldList ) {
if( item.getInt > 5) {
newList.add(item);
}
}
And to me the first way looks a bit smarter.
Java 7 might or might not implement closures and hence support functionality like this, but currently it doesn't, so on the Java VM you have the options to do it in Groovy, Scala or Clojure (possible others, too), but in java you can only get close to that by using helpers like Guava's Collections2.filter().
JDK 7 sample code:
findItemsLargerThan(List<Integer> l, int what){
return filter(boolean(Integer x) { x > what }, l);
}
findItemsLargerThan(Arrays.asList(1,2,5,6,9), 5)
Groovy sample code:
Arrays.asList(1,2,5,6,9).findAll{ it > 5}
Guava Sample Code:
Collections2.filter(Arrays.asList(1, 2, 5, 6, 9),
new Predicate<Integer>(){
#Override
public boolean apply(final Integer input){
return input.intValue() > 5;
}
}
);
Scala sample code (thanks Bolo):
Array(1, 2, 5, 6, 9) filter (x => x > 5)
You can give a look at lambdaj. There is a select method you can use with a hamcrest condition.
Nothing is impossible (-:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ListCleaner {
public static void main(String[] args) {
final List<Integer> oldList = Arrays.asList(new Integer[] { 23, 4, 5,
657 });
System.out.println(oldList);
List<Integer> newList = new ArrayList<Integer>() {
{
for (Integer element : oldList) {
if (element > 5) {
this.add(element);
}
}
}
};
System.out.println(newList);
}
}
The only constraint is that the oldList has to be final.
It can be done in pure Java, but you need to write a support class Filter for the following code to run successfully:
List<Integer> oldList = Arrays.asList(new Integer[] { 1, 2, 5, 6, 9 });
List<Integer> newList = new Filter<Integer>(oldList) {
{
findAll(it > 5);
}
}.values();
System.out.println(newList); // [6, 9]
In case you wonder why this code compiles take a look at Hidden features of Java: Double brace initialization. This creates an anonymous instance of the class Filter that contains the it variable and provides the method findAll().
The Filter class itself has the one drawback that a new instance is created for each list element to evaluate the boolean condition at findAll():
public abstract class Filter<T> {
protected List<T> values = new ArrayList<T>();
protected T it;
public Filter(List<T> values) {
if (values != null) {
this.values.addAll(values);
}
if (values.isEmpty()) {
throw new RuntimeException("Not for empty collections!");
}
it = values.iterator().next();
// instance initializer gets executed here, calls findAll
}
protected void findAll(boolean b) throws Throwable {
// exit condition for future calls
if (values.size() > 1) {
// only repeat for each entry, if values has multiple entries
Iterator<T> iterator = values.iterator();
while (iterator.hasNext()) {
// don't evalute again for the first entry
if (!b) {
iterator.next();
iterator.remove();
b = true;
} else {
// for each other entry create an argument with one element
List<T> next = new ArrayList<T>();
next.add(iterator.next());
// get constructor of anonymous class
Constructor<?> constructor = this.getClass().getDeclaredConstructors()[0];
// invoke constructor and thus execute instance initializer again
Filter<T> filtered = (Filter<T>) constructor.newInstance(new Object[] { null, next });
// if values is empty, the condition didn't match and the element can be removed
if (filtered.values.isEmpty()) {
iterator.remove();
}
}
}
} else {
// one element can be checked directly
if (!b) {
values.clear();
}
}
}
public List<T> values() {
return values;
}
}
But as instance creation is rather cheap these days and the Filter class is usable for all Objects, it may be worth including in your Utils package.
Greetz,
GHad
No, this kind of dynamic language construct is not supported in Java yet :-) So you have to live with your option 2

Comparator interface

ok I was going to edit my previous question but i wasnt sure if it was the right way to do it so i'll just give another question about Comparator, now i want to be able to sort with different ways. I have a bank checks and i want to sort with checkNumber then checkAmount
i managed to do it with checkNumber but couldnt figure out how with checkAmount
here is how i did it for checkNumber:
import java.util.Comparator;
public class Check implements Comparator {
private int checkNumber;
private String description;
private double checkAmount;
public Check() {
}
public Check(int newCheckNumber, double newAmountNumber) {
setCheckNumber(newCheckNumber);
setAmountNumber(newAmountNumber);
}
public String toString() {
return checkNumber + "\t\t" + checkAmount;
}
public void setCheckNumber(int checkNumber) {
this.checkNumber = checkNumber;
}
public int getCheckNumber() {
return checkNumber;
}
public void setAmountNumber(double amountNumber) {
this.checkAmount = amountNumber;
}
public double getAmountNumber() {
return checkAmount;
}
#Override
public int compare(Object obj1, Object obj2) {
int value1 = ((Check) obj1).getCheckNumber();
int value2 = ((Check) obj2).getCheckNumber();
int result = 0;
if (value1 > value2){
result = 1;
}
else if(value1 < value2){
result = -1;
}
return result;
}
}
import java.util.ArrayList;
import java.util.Collections;
import test.CheckValue;
public class TestCheck {
public static void main(String[] args) {
ArrayList List = new ArrayList();
List.add(new Check(445, 55.0));
List.add(new Check(101,43.12));
List.add(new Check(110,101.0));
List.add(new Check(553,300.21));
List.add(new Check(123,32.1));
Collections.sort(List, new Check());
System.out.println("Check Number - Check Amount");
for (int i = 0; i < List.size(); i++){
System.out.println(List.get(i));
}
}
}
thank you very much in advance and please tell me if im submiting things in the wrong way.
What you really want to do is define a separate class to act as the Comparator object - don't make your actual Check class the comparator, but instead have 3 classes:
the Check class itself
a CheckAmountComparator class (or something similar) that implements Comparator<Check>
a CheckNumberComparator class (or something similar) that implements Comparator<Check>
Then when you want to sort one way or another, you simply pass an instance of the Comparator-implementing class corresponding to the type of sorting you want to do. For instance, to sort by amount, it'd then become...
Collections.sort(yourListVariable, new CheckAmountComparator());
Also - I'd highly suggest naming your variable something other than List, since List is used as a type name in Java.
You should make Check implements Comparable<Check>, but not itself implements Comparator.
A Comparable type defines the natural ordering for the type, and a Comparator for a type is usually not the type itself, and defines their own custom ordering of that type.
Related questions
When to use Comparable vs Comparator
Java: What is the difference between implementing Comparable and Comparator?
Can I use a Comparator without implementing Comparable?
Also, you shouldn't use raw type. You need to use parameterized generic types, Comparable<Check>, Comparator<Check>, List<Check>, etc.
Related questions
What is a raw type and why shouldn’t we use it?
A String example
Let's take a look at what String has:
public final class String implements Comparable<String>
String defines its natural ordering as case-sensitive
It has a field
public static final Comparator<String> CASE_INSENSITIVE_ORDER
Here we have a case-insensitive custom Comparator<String>
An example of using this is the following:
List<String> list = new ArrayList<String>(
Arrays.asList("A", "B", "C", "aa", "bb", "cc")
);
Collections.sort(list);
System.out.println(list);
// prints "[A, B, C, aa, bb, cc]"
Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
System.out.println(list);
// prints "[A, aa, B, bb, C, cc]"
Here's an example of sorting List<String> using both its natural ordering and your own custom Comparator<String>. Note that we've defined our own Comparator<String> without even changing the final class String itself.
List<String> list = new ArrayList<String>(
Arrays.asList("1", "000000", "22", "100")
);
Collections.sort(list);
System.out.println(list);
// prints "[000000, 1, 100, 22]" natural lexicographical ordering
Comparator<String> lengthComparator = new Comparator<String>() {
#Override public int compare(String s1, String s2) {
return Integer.valueOf(s1.length())
.compareTo(s2.length());
}
};
Collections.sort(list, lengthComparator);
System.out.println(list);
// prints "[1, 22, 100, 000000]" ordered by length
Comparator<String> integerParseComparator = new Comparator<String>() {
#Override public int compare(String s1, String s2) {
return Integer.valueOf(Integer.parseInt(s1))
.compareTo(Integer.parseInt(s2));
}
};
Collections.sort(list, integerParseComparator);
System.out.println(list);
// prints "[000000, 1, 22, 100]" ordered by their values as integers
Conclusion
You can follow the example set by String, and do something like this:
public class Check implements Comparable<Check> {
public static final Comparator<Check> NUMBER_ORDER = ...
public static final Comparator<Check> AMOUNT_ORDER = ...
public static final Comparator<Check> SOMETHING_ELSE_ORDER = ...
}
Then you can sort a List<Check> as follows:
List<Check> checks = ...;
Collections.sort(checks, Check.AMOUNT_ORDER);

Python-like list comprehension in Java

Since Java doesn't allow passing methods as parameters, what trick do you use to implement Python like list comprehension in Java ?
I have a list (ArrayList) of Strings. I need to transform each element by using a function so that I get another list. I have several functions which take a String as input and return another String as output. How do I make a generic method which can be given the list and the function as parameters so that I can get a list back with each element processed. It is not possible in the literal sense, but what trick should I use ?
The other option is to write a new function for each smaller String-processing function which simply loops over the entire list, which is kinda not so cool.
In Java 8 you can use method references:
List<String> list = ...;
list.replaceAll(String::toUpperCase);
Or, if you want to create a new list instance:
List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList());
Basically, you create a Function interface:
public interface Func<In, Out> {
public Out apply(In in);
}
and then pass in an anonymous subclass to your method.
Your method could either apply the function to each element in-place:
public static <T> void applyToListInPlace(List<T> list, Func<T, T> f) {
ListIterator<T> itr = list.listIterator();
while (itr.hasNext()) {
T output = f.apply(itr.next());
itr.set(output);
}
}
// ...
List<String> myList = ...;
applyToListInPlace(myList, new Func<String, String>() {
public String apply(String in) {
return in.toLowerCase();
}
});
or create a new List (basically creating a mapping from the input list to the output list):
public static <In, Out> List<Out> map(List<In> in, Func<In, Out> f) {
List<Out> out = new ArrayList<Out>(in.size());
for (In inObj : in) {
out.add(f.apply(inObj));
}
return out;
}
// ...
List<String> myList = ...;
List<String> lowerCased = map(myList, new Func<String, String>() {
public String apply(String in) {
return in.toLowerCase();
}
});
Which one is preferable depends on your use case. If your list is extremely large, the in-place solution may be the only viable one; if you wish to apply many different functions to the same original list to make many derivative lists, you will want the map version.
The Google Collections library has lots of classes for working with collections and iterators at a much higher level than plain Java supports, and in a functional manner (filter, map, fold, etc.). It defines Function and Predicate interfaces and methods that use them to process collections so that you don't have to. It also has convenience functions that make dealing with Java generics less arduous.
I also use Hamcrest** for filtering collections.
The two libraries are easy to combine with adapter classes.
** Declaration of interest: I co-wrote Hamcrest
Apache Commons CollectionsUtil.transform(Collection, Transformer) is another option.
I'm building this project to write list comprehension in Java, now is a proof of concept in https://github.com/farolfo/list-comprehension-in-java
Examples
// { x | x E {1,2,3,4} ^ x is even }
// gives {2,4}
Predicate<Integer> even = x -> x % 2 == 0;
List<Integer> evens = new ListComprehension<Integer>()
.suchThat(x -> {
x.belongsTo(Arrays.asList(1, 2, 3, 4));
x.is(even);
});
// evens = {2,4};
And if we want to transform the output expression in some way like
// { x * 2 | x E {1,2,3,4} ^ x is even }
// gives {4,8}
List<Integer> duplicated = new ListComprehension<Integer>()
.giveMeAll((Integer x) -> x * 2)
.suchThat(x -> {
x.belongsTo(Arrays.asList(1, 2, 3, 4));
x.is(even);
});
// duplicated = {4,8}
You can use lambdas for the function, like so:
class Comprehension<T> {
/**
*in: List int
*func: Function to do to each entry
*/
public List<T> comp(List<T> in, Function<T, T> func) {
List<T> out = new ArrayList<T>();
for(T o: in) {
out.add(func.apply(o));
}
return out;
}
}
the usage:
List<String> stuff = new ArrayList<String>();
stuff.add("a");
stuff.add("b");
stuff.add("c");
stuff.add("d");
stuff.add("cheese");
List<String> newStuff = new Comprehension<String>().comp(stuff, (a) -> { //The <String> tells the comprehension to return an ArrayList<String>
a.equals("a")? "1":
(a.equals("b")? "2":
(a.equals("c")? "3":
(a.equals("d")? "4": a
)))
});
will return:
["1", "2", "3", "4", "cheese"]
import java.util.Arrays;
class Soft{
public static void main(String[] args){
int[] nums=range(9, 12);
System.out.println(Arrays.toString(nums));
}
static int[] range(int low, int high){
int[] a=new int[high-low];
for(int i=0,j=low;i<high-low;i++,j++){
a[i]=j;
}
return a;
}
}
Hope, that I help you :)

Categories

Resources