Java Lambda: Iterate over 2 dim-array keeping the current index - java

I'm new to Java 8's Lambda Expressions and I want to formulate the following:
I have a 2-dimensional array which I want to iterate over several times in my application code and do stuff with the items in the array. Before i'd do the following:
public static abstract class BlaBlaIterator {
private final BlaBla[][] blabla;
public BlaBlaIterator(final BlaBla[][] blabla) {
this.blabla = blabla;
}
public void iterate() {
final int size = blabla.length;
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
final BlaBla bla = blabla[x][y];
iterateAction(x, y, bla, bla == null);
}
}
}
public abstract void iterateAction(int x, int y, BlaBla bla, boolean isNull);
}
and then
BlaBla[][] blabla = ...
new BlaBlaIterator(blabla) {
#Override
public void iterateAction(final int x, final int y, final BlaBla bla, final boolean isNull) {
//...
}
}.iterate();
Crucial thing: I need access to the current x/y and I need to get calculated things like the isNull.
What I want to do now is to convert this to lambda. I want to write something like this:
BlaBla[] blabla = ...
blabla.stream().forEach((x, y, blabla, isNull) -> ... );
To get a stream from the 2-dimensional Array I can do
Arrays.stream(field).flatMap(x -> Arrays.stream(x))
But then I loose the x/y info and cannot pass calculated stuff like isNull. How can i do this?

To be honest I would keep the traditionnal nested loop, IMO this is a much cleaner approach. Streams are not a substition for all the "old" Java code. Nevertheless, I posted some possible approaches.
First approach
Here's a first possible approach (Object-oriented). Create a class ArrayElement to hold the indices:
class ArrayElement<V> {
public final int row;
public final int col;
public final V elem;
...
}
Then you'll need to create a method that creates a Stream of elements from a single array (the one that we will call for flatMap), and iterateAction just print out the current instance
private static <T> Stream<ArrayElement<T>> createStream(int row, T[] arr) {
OfInt columns = IntStream.range(0, arr.length).iterator();
return Arrays.stream(arr).map(elem -> new ArrayElement<>(row, columns.nextInt(), elem));
}
private static <V> void iterateAction(ArrayElement<V> elem) {
System.out.println(elem);
}
Finally the main looks like this:
String[][] arr = {{"One", "Two"}, {"Three", "Four"}};
OfInt rows = IntStream.range(0, arr.length).iterator();
Arrays.stream(arr)
.flatMap(subArr -> createStream(rows.nextInt(), subArr))
.forEach(Main::iterateAction);
and outputs:
ArrayElement [row=0, col=0, elem=One]
ArrayElement [row=0, col=1, elem=Two]
ArrayElement [row=1, col=0, elem=Three]
ArrayElement [row=1, col=1, elem=Four]
This solution has the disadvantage that it creates a new Object for each Object in the array.
Second approach
The second approach is more direct, it's the same idea but you don't create a new ArrayElement instance for each elem in the array. Again this could be done in a one liner but the lamdba would become ugly so I splitted those up in methods (like in the first approach):
public class Main {
public static void main(String[] args) {
String[][] arr = { {"One", "Two"}, {null, "Four"}};
OfInt rows = IntStream.range(0, arr.length).iterator();
Arrays.stream(arr).forEach(subArr -> iterate(subArr, rows.nextInt()));
}
static <T> void iterate(T[] arr, int row) {
OfInt columns = IntStream.range(0, arr.length).iterator();
Arrays.stream(arr).forEach(elem -> iterateAction(row, columns.nextInt(), elem, elem == null));
}
static <T> void iterateAction(int x, int y, T elem, boolean isNull) {
System.out.println(x+", "+y+", "+elem+", "+isNull);
}
}
and it outputs:
0, 0, One, false
0, 1, Two, false
1, 0, null, true
1, 1, Four, false
Third approach
Using two instances of AtomicInteger
String[][] arr = {{"One", "Two"}, {null, "Four"}};
AtomicInteger rows = new AtomicInteger();
Arrays.stream(arr).forEach(subArr -> {
int row = rows.getAndIncrement();
AtomicInteger colums = new AtomicInteger();
Arrays.stream(subArr).forEach(e -> iterateAction(row, colums.getAndIncrement(), e, e == null));
});
which produces the same output as above.
My conclusion
It's duable using Streams but I really prefer the nested loop in your use-case since you need both the x and y values.

This is an issue, similar to the different forms of for-loop. If you are not interested in the indices, you can imply say:
for(BlaBla[] array: blabla) for(BlaBla element: array) action(element);
But if you are interested in the indices, you can’t use the for-each loop but have to iterate over the indices and get the array element in the loop body. Similarly, you have to stream the indices when using Stream and need the indices:
IntStream.range(0, blabla.length)
.forEach(x -> IntStream.range(0, blabla[x].length)
.forEach(y -> {
final BlaBla bla = blabla[x][y];
iterateAction(x, y, bla, bla == null);
})
);
This is a 1:1 translation which has the advantage of not requiring additional classes, but it consists of two distinct Stream operations rather than one fused operation, as would be preferred.
A single, fused operation might look like this:
helped class:
class ArrayElement {
final int x, y;
BlaBla element;
final boolean isNull;
ArrayElement(int x, int y, BlaBla obj) {
this.x=x; this.y=y;
element=obj; isNull=obj==null;
}
}
actual operation:
IntStream.range(0, blabla.length).boxed()
.flatMap(x -> IntStream.range(0, blabla[x].length)
.mapToObj(y->new ArrayElement(x, y, blabla[x][y])))
.forEach(e -> iterateAction(e.x, e.y, e.element, e.isNull));

Related

Access directly a column inside an ArrayList in Java

I am trying to access not a single element inside a 2D ArrayList but an entire column. Since it is very easy to do that when you need a row (since the 2D Array is read by row and columns), it was not so trivial to me to do the same for a column.
Do you have any suggestion relevant to that?
Any kind of help will be very appreciated!
Many thanks!
There is no way to directly access a column in a 2-D row-oriented list. You can, however, always extract the required value.
To retrieve, column at index n, you can simply run:
twoDList.stream().map(innerList -> innerList.get(n))
.collect(Collectors.toList());
This can also be easily changed to a normal for-each loop.
List<T> column = new ArrayList<T>();
for(List<T> row: twoDList) {
column.add(row.get(n));
}
I posted something similar here recently.
Here I create a ColumnList object that allows me to manipulate a single column of a 2D array. In this case I am sorting it.
class ColumnList<T> extends AbstractList<T> implements List<T> {
private final T[][] array;
private final int column;
public ColumnList(T[][] array, int column) {
this.array = array;
this.column = column;
}
#Override
public T get(int index) {
return array[index][column];
}
#Override
public T set(int index, T element) {
return array[index][column] = element;
}
#Override
public int size() {
return array.length;
}
}
public void test(String[] args) {
Integer[][] array = {
{6, 8, 9, 16},
{0, 6, -3, 4},
{18, 2, 1, 11}
};
System.out.println("Before: " + Arrays.deepToString(array));
// Sort each column separately.
for (int i = 0; i < array[0].length; i++) {
List<Integer> column = new ColumnList<>(array, i);
Collections.sort(column);
}
System.out.println("After: " + Arrays.deepToString(array));
}
The best way is to use a List within a List:
List<List<String>> listWithList = new ArrayList<List<String>>();
To get the child List from the parent List you need to call for example 0 for the first child List:
List<String> anotherList = new ArrayList<String>();
anotherList = listWithList.get(0); // get the first complete List
To get a String from the child List you need to call:
String someText = anotherList.get(0); // get the first String
or directly from the List within the List:
String moreText = listWithList.get(0).get(0); // get the first String from the first List

How to iterate over every n elements with java?

I have a method named calculate and it takes too long to complete. So I decided to send my info list objects to this method partially. How can I iterate over every n elements?
public static void main(String [] args){
Map<String, Long> info....; //my info Map
//I want to call method like
for(int i = 0; i<info.size(); i+=5)
calculate(info.submap(i,i+5));
}
public static boolean calculate(Map<String, Long> info){
//Some calculations
}
You can use following code
class SomeClass {
private final int BUFFER_SIZE = 5;
public static void main(String[] args) {
Map<String, Long> info = new HashMap<>();
LongStream.range(0, 30).boxed().forEach(i -> info.put("key" + i, i)); // for test
IntStream.range(0, info.size() / BUFFER_SIZE)
.boxed()
.parallel()
.map(i -> Arrays.copyOfRange(info.keySet().toArray(), BUFFER_SIZE * i, BUFFER_SIZE * (i + 1)))
.map(Arrays::asList)
.map(keys -> info.entrySet().stream()
.filter(x -> keys.contains(x.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))
.forEach(SomeClass::calculate);
}
public static boolean calculate(Map<String, Long> info) {
System.out.println("calculation for " + info.toString());
return true;
}
}
It sounds like what you want to do is to implement a sort of batch processing for the data represented by your Map<String, Long> info instance. You can then create a generator for these batches as a Stream: This is in a way the inverse of the Stream.flatMap(...) family of methods, but, ironically, there doesn't seem to be any idiomatic functional way of doing this and so you may have to create the batches yourself in an imperative manner — for example:
private static <T> Stream<Stream<T>> createBatchStreams(final Iterator<T> iter, final int maxBatchSize) {
final Stream.Builder<Stream<T>> resultBuilder = Stream.builder();
{
// NOTE: This logic could also be encapsulated in a Collector class
// in order to make it less imperative style
Stream.Builder<T> currentBatchBuilder = Stream.builder();
int currentBatchSize = 0;
while (iter.hasNext()) {
final T next = iter.next();
if (currentBatchSize == maxBatchSize) {
resultBuilder.add(currentBatchBuilder.build());
// Start building a new batch
currentBatchBuilder = Stream.builder();
currentBatchSize = 0;
}
currentBatchBuilder.add(next);
currentBatchSize++;
}
// Check if there is a non-empty Stream to add (e.g. if there was a
// final batch which was smaller than the others)
if (currentBatchSize > 0) {
resultBuilder.add(currentBatchBuilder.build());
}
}
return resultBuilder.build();
}
Using this method, you can then create a generator of batches of your map data, which can then be fed to your calculate(...) function (albeit with a slightly different signature):
public static void main(final String[] args) {
final Map<String, Long> info = LongStream.range(0, 10).boxed()
.collect(Collectors.toMap(value -> "key" + value, Function.identity())); // Test data
final Stream<Stream<Entry<String, Long>>> batches = createBatchStreams(info.entrySet().iterator(), 5);
batches.forEach(batch -> {
calculate(batch);
// Do some other stuff after processing each batch
});
}
private static boolean calculate(final Stream<Entry<String, Long>> info) {
// Some calculations
}

How to filter list of lists using streams on the basis of a value in a list?

I have a List<List<Double>>. I want to filter rows on the basis of an index i.e. if value of an element at index 4 is less than 0.2 then, skip that row? The resultant List<List<Double>> should be smaller or same as the input one.
You can use Stream.filter. Note you have to select the rows which you want to take, not the rows which you want to skip:
List<List<Double>> input = ...;
List<List<Double>> result = input.stream()
.filter(row -> row.get(4) >= 0.2)
.collect(Collectors.toList());
An alternative to the Stream API would be in-place modification using Collection.removeIf:
List<List<Double>> input = ...;
input.removeIf(row -> row.get(4) < 0.2);
You could use a lambda expression, and that would be fine, but for something a little more reusable, you might consider using your own Predicate, for example
public class SubElementPredict implements Predicate<List<Double>> {
private int index;
private double value;
public SubElementPredict(int index, double value) {
this.index = index;
this.value = value;
}
#Override
public boolean test(List<Double> t) {
return value < t.get(index);
}
}
Then you could just do something like...
List<List<Double>> values = new ArrayList<>(25);
values.add(Arrays.asList(new Double[]{1d, 2d, 3d, 4d, 5d}));
values.add(Arrays.asList(new Double[]{6d, 7d, 8d, 9d, 10d}));
values.add(Arrays.asList(new Double[]{11d, 12d, 13d, 14d, 15d}));
int index = 2;
double value = 8d;
List<List<Double>> filtered = values
.stream()
.filter(new SubElementPredict(index, value))
.collect(Collectors.toList());
for (List<Double> sub : filtered) {
System.out.println(sub);
}
Which outputs
[11.0, 12.0, 13.0, 14.0, 15.0]
Now, if you wanted to get even more adventures, you might even be able to do something like...
public class SubElementPredict<V extends Comparable> implements Predicate<List<V>> {
private int index;
private V value;
public SubElementPredict(int index, V value) {
this.index = index;
this.value = value;
}
#Override
public boolean test(List<V> t) {
return value.compareTo(t.get(index)) <= 0;
}
}
And now, you can use any List of Comparable values

How to get random objects from a stream

Lets say I have a list of words and i want to create a method which takes the size of the new list as a parameter and returns the new list. How can i get random words from my original sourceList?
public List<String> createList(int listSize) {
Random rand = new Random();
List<String> wordList = sourceWords.
stream().
limit(listSize).
collect(Collectors.toList());
return wordList;
}
So how and where can I use my Random?
I've found a proper solution.
Random provides a few methods to return a stream. For example ints(size) which creates a stream of random integers.
public List<String> createList(int listSize)
{
Random rand = new Random();
List<String> wordList = rand.
ints(listSize, 0, sourceWords.size()).
mapToObj(i -> sourceWords.get(i)).
collect(Collectors.toList());
return wordList;
}
I think the most elegant way is to have a special collector.
I am pretty sure the only way you can guarantee that each item has an equal chance of being picked, is to collect, shuffle and re-stream. This can be easily done using built-in Collectors.collectingAndThen(...) helper.
Sorting by a random comparator or using randomized reducer, like suggested on some other answers, will result in very biased randomness.
List<String> wordList = sourceWords.stream()
.collect(Collectors.collectingAndThen(Collectors.toList(), collected -> {
Collections.shuffle(collected);
return collected.stream();
}))
.limit(listSize)
.collect(Collectors.toList());
You can move that shuffling collector to a helper function:
public class CollectorUtils {
public static <T> Collector<T, ?, Stream<T>> toShuffledStream() {
return Collectors.collectingAndThen(Collectors.toList(), collected -> {
Collections.shuffle(collected);
return collected.stream();
});
}
}
I assume that you are looking for a way to nicely integrate with other stream processing functions. So following straightforward solution is not what you are looking for :)
Collections.shuffle(wordList)
return wordList.subList(0, limitSize)
This is my one line solution:
List<String> st = Arrays.asList("aaaa","bbbb","cccc");
st.stream().sorted((o1, o2) -> RandomUtils.nextInt(0, 2)-1).findFirst().get();
RandomUtils are from commons lang 3
Here's a solution I came up with which seems to differ from all the other ones, so I figured why not add it to the pile.
Basically it works by using the same kind of trick as one iteration of Collections.shuffle each time you ask for the next element - pick a random element, swap that element with the first one in the list, move the pointer forwards. Could also do it with the pointer counting back from the end.
The caveat is that it does mutate the list you passed in, but I guess you could just take a copy as the first thing if you didn't like that. We were more interested in reducing redundant copies.
private static <T> Stream<T> randomStream(List<T> list)
{
int characteristics = Spliterator.SIZED;
// If you know your list is also unique / immutable / non-null
//int characteristics = Spliterator.DISTINCT | Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.SIZED;
Spliterator<T> spliterator = new Spliterators.AbstractSpliterator<T>(list.size(), characteristics)
{
private final Random random = new SecureRandom();
private final int size = list.size();
private int frontPointer = 0;
#Override
public boolean tryAdvance(Consumer<? super T> action)
{
if (frontPointer == size)
{
return false;
}
// Same logic as one iteration of Collections.shuffle, so people talking about it not being
// fair randomness can take that up with the JDK project.
int nextIndex = random.nextInt(size - frontPointer) + frontPointer;
T nextItem = list.get(nextIndex);
// Technically the value we end up putting into frontPointer
// is never used again, but using swap anyway, for clarity.
Collections.swap(list, nextIndex, frontPointer);
frontPointer++;
// All items from frontPointer onwards have not yet been chosen.
action.accept(nextItem);
return true;
}
};
return StreamSupport.stream(spliterator, false);
}
Try something like that:
List<String> getSomeRandom(int size, List<String> sourceList) {
List<String> copy = new ArrayList<String>(sourceList);
Collections.shuffle(copy);
List<String> result = new ArrayList<String>();
for (int i = 0; i < size; i++) {
result.add(copy.get(i));
}
return result;
}
The answer is very simple(with stream):
List<String> a = src.stream().sorted((o1, o2) -> {
if (o1.equals(o2)) return 0;
return (r.nextBoolean()) ? 1 : -1;
}).limit(10).collect(Collectors.toList());
You can test it:
List<String> src = new ArrayList<String>();
for (int i = 0; i < 20; i++) {
src.add(String.valueOf(i*10));
}
Random r = new Random();
List<String> a = src.stream().sorted((o1, o2) -> {
if (o1.equals(o2)) return 0;
return (r.nextBoolean()) ? 1 : -1;
}).limit(10).collect(Collectors.toList());
System.out.println(a);
If you want non repeated items in the result list and your initial list is immutable:
There isn't a direct way to get it from the current Streams API.
It's not possible to use a random Comparator because it's going to break the compare contract.
You can try something like:
public List<String> getStringList(final List<String> strings, final int size) {
if (size < 1 || size > strings.size()) {
throw new IllegalArgumentException("Out of range size.");
}
final List<String> stringList = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
getRandomString(strings, stringList)
.ifPresent(stringList::add);
}
return stringList;
}
private Optional<String> getRandomString(final List<String> stringList, final List<String> excludeStringList) {
final List<String> filteredStringList = stringList.stream()
.filter(c -> !excludeStringList.contains(c))
.collect(toList());
if (filteredStringList.isEmpty()) {
return Optional.empty();
}
final int randomIndex = new Random().nextInt(filteredStringList.size());
return Optional.of(filteredStringList.get(randomIndex));
}
#kozla13 improved version:
List<String> st = Arrays.asList("aaaa","bbbb","cccc");
st.stream().min((o1, o2) -> o1 == o2 ? 0 : (ThreadLocalRandom.current().nextBoolean() ? -1 : 1)).orElseThrow();
Used java built-in class ThreadLocalRandom
nextInt generates one from sequence [-1, 0, 1], but return 0 in compare func means equals for the elements and side effect of this - first element (o1) will be always taken in this case.
properly handle object equals case
A stream is probably overkill. Copy the source list so you're not creating side-effects, then give back a sublist of the shuffled copy.
public static List<String> createList(int listSize, List<String> sourceList) {
if (listSize > sourceList.size()) {
throw IllegalArgumentException("Not enough words for new list.");
}
List<String> copy = new ArrayList<>(sourceList);
Collections.shuffle(copy);
return copy.subList(0, listSize);
}
If the source list is generally much larger than the new list, you might gain some efficiencies by using a BitSet to get random indices:
List<String> createList3(int listSize, List<String> sourceList) {
if (listSize > sourceList.size()) {
throw new IllegalArgumentException("Not enough words in the source list.");
}
List<String> newWords = randomWords(listSize, sourceList);
Collections.shuffle(newWords); // optional, for random order
return newWords;
}
private List<String> randomWords(int listSize, List<String> sourceList) {
int endExclusive = sourceList.size();
BitSet indices = new BitSet(endExclusive);
Random rand = new Random();
while (indices.cardinality() < listSize) {
indices.set(rand.nextInt(endExclusive));
}
return indices.stream().mapToObj(i -> sourceList.get(i))
.collect(Collectors.toList());
}
One liner to randomize a stream:
Stream.of(1, 2, 3, 4, 5).sorted(Comparator.comparingDouble(x -> Math.random()))

sorting a List<List<Integer>> using java Collections.sort()

I have a list of list as follow:
List<List<Integer>> matchedPostions = findTerms(originalEntPos, singularEntPos, singText);
Consider this example
[ID,StartPostion,EndPostion]
^^^
[1,198,200]
[2,50,61]
I am trying to sort list using Collections.sort(). How can I sort the values inside the matchedPostions based on the StartPostion values, from lower to higher values?
You'll need to implement a Comparator to sort custom data-structures like the one you provided.
import static java.util.Arrays.asList;
List<List<Integer>> matchedPostions = asList(asList(1, 198, 200), asList(2, 50, 61));
Collections.sort(matchedPostions, new Comparator<List<Integer>>() {
#Override
public int compare(List<Integer> o1, List<Integer> o2) {
// Sort the lists using the starting position (second element in the list)
return o1.get(1).compareTo(o2.get(1));
}
});
System.out.println(matchedPostions);
// [[2, 50, 61], [1, 198, 200]]
This is the "dirty" way. The more idiomatic approach is described Duncan where you implement a Range class which properly encapsulates your data.
I would strongly recommend you create a class to hold your list values. This allows you to enjoy the benefits of type safety, including being sure you always have exactly two integer values (rather than a unknown number of items in a list). For instance:
public class Range implements Comparable<Range> {
private final int startPosition;
private final int endPosition;
public Range(int startPosition, int endPosition) {
this.startPosition = startPosition;
this.endPosition = endPosition;
}
#Override
public int compareTo(Range o) {
return startPosition - o.startPosition;
}
#Override
public String toString() {
return String.format("[%d,%d]", startPosition, endPosition);
}
}
Because this class implements Comparable, you can sort with the normal Collections.sort method:
public static void main(String[] args) throws Exception {
List<Range> ranges = Arrays.asList(new Range(198, 200), new Range(50,
61));
System.out.println("Unsorted");
for (Range range : ranges) {
System.out.println(range);
}
Collections.sort(ranges);
System.out.println("Sorted");
for (Range range : ranges) {
System.out.println(range);
}
}
Output:
Unsorted
[198,200]
[50,61]
Sorted
[50,61]
[198,200]
For the inner lists, you can just loop over them:
for(List<Integer> inner : outer){
Collections.sort(inner);
}
For the outer List, you need a custom Comparator.
If you can't define your own specialized classes for ranges, your can call Collections.sort with a your own Comparator. Example is below:
Collections.sort(list, new Comparator<List<Integer>>() {
#Override
public int compare(List<Integer> l1, List<Integer> l2) {
return l1.get(0).compareTo(l2.get(0));
}
});
Using sort() in java you can sort easily using lambda function: List<List> ans = new LinkedList<>();
ans.sort((x, y) -> {
for (int i = 0; i < Math.min(x.size(), y.size()); i++) {
if (x.get(i) != y.get(i)) {
return x.get(i) - y.get(i);
}
}
return x.size() - y.size();
});
Its another way for sorting List of List of Integer using lambda function
[Upvote for more such Java solution ♨︎].

Categories

Resources