Change all position of elements in List using Java 8 - java

I am a fresher in Java. I just want to change all position of elements in List using Java 8. For example:
public static List<Integer> change(List<Integer> data){
for (int i = 0; i < data.size(); i++) {
Collections.swap(data, i, data.size()-1-i);
}
return data;
}
this is a simple example, it just reverse the list. let me explain more :
i have a list {1,3,4,5,3,7,8,9} >> and i want to change to {3,1,5,4,7,3,9,8}.
But I want to do it in Java 8(Stream). My problem is:
1) How can I get the next element in a stream ?
2) Can I put my own method when I traverse with stream? (for example, I can write my own swap method?) - Like : list.foreach(doSomething());
3) And how can i return result when using foreach?

Try this :
List<String> list = Arrays.asList("A", "B", "C", "D", "1", "2", "3");
// shuffle or randomize
Collections.shuffle(list);

If you want to shuffle in such a way that no item is where it was before then you can simply use
Collections.reverse(list);
and then swap the 1st and the middle item if the length of the list is odd. This way all your items will be at a different index then they were before.

If using streams is required
public static List<Integer> change2( List<Integer> data ) {
return data.stream().collect( LinkedList::new, LinkedList::offerFirst, LinkedList::addAll );
}
The stream is reversed using a LinkedList to collect data ; offerFirst inserting next element in front.
Or with a custom function
final Random random = new Random();
return data.stream().collect(
LinkedList::new,
( list, elem ) -> {
int size = list.size();
list.add( random.nextInt( size + 1 ), elem );
},
LinkedList::addAll
);

You can use the "forEach" that Streams provide.
by saying "a->" you map each element of the data list to the "a" where is every element of the list
This is the best solution if you dont want just to shuffle them but do something more with each one of the elements of the list.
public static List<Integer> change(List<Integer> data){
return data.stream().forEach(a-> "whatever you want to do with them").collect(Collectors.toList());
}

One way is to drive your list through an IntStream and swap elements within forEach:
public static List<Integer> change(List<Integer> data) {
IntStream.iterate(0, i -> i + 2)
.limit(data.size() / 2)
.forEach(i -> Collections.swap(data, i, i + 1));
return data;
}
You have to use Stream.limit so that the stream is not infinite.
However, using Collections.swap to mutate the list within forEach is discouraged because it has side-effects. If the IntStream were declared to be parallel, the code above would be broken. Ideally, the functions and consumers used in a stream should be stateless and without side-effects, as recommended in the java.util.stream package documentation.
Here's another way to achieve what you want, while also adhering to recommendations:
public static List<Integer> changeCorrectly(List<Integer> data) {
return IntStream.iterate(0, i -> i + 2)
.limit(data.size() / 2)
.flatMap(i -> IntStream.of(data.get(i + 1), data.get(i)))
.boxed()
.collect(Collectors.toList());
}
Note: I'm using IntStream.flatMap to return an IntStream of swapped pairs, which has the effect to unbox Integer elements from the data list. Immediately, I'm boxing the IntStream to a Stream<Integer>.
The alternative to avoid this unboxing/boxing stuff is:
public static List<Integer> changeCorrectly(List<Integer> data) {
return IntStream.iterate(0, i -> i + 2)
.limit(data.size() / 2)
.mapToObj(i -> Stream.of(data.get(i + 1), data.get(i)))
.flatMap(Function.identity())
.collect(Collectors.toList());
}
Final comment: this only works when the data list has an even size. Handling the addition of the last element when the data list has an odd size is left as an exercise.

Related

Implement conditional reduction of two lists into a single list containing two elements using Stream API

I'm comparing two lists based on the following conditions:
If list1 has higher value, +1 to the first element in the result.
If list2 has higher value, +1 to the second element in the result.
If values are the same, skip it.
I have implemented this solution in Java 7
int min = a.size() > b.size() ? b.size() : a.size();
List<Integer> result = Arrays.asList(0, 0);
for(int i =0; i < min; i++) {
if(a.get(i) > b.get(i))
result.set(0, result.get(0) + 1);
else if(a.get(i) < b.get(i))
result.set(1, result.get(1) + 1);
}
return result;
How could I do the same with Java 8 streams?
I came up with two streams and merging the results of both. Because I am not sure how to do both the conditions in single streams.
Using range() method of the IntStream we can create a stream of indices that are valid for these lists.
To filter out indices for which values are not equal, we can apply filter() operation.
Accumulating the result in a list comprised of two elements will require a bit more effort. For that we can use collect(supplier,accumulator,combiner):
supplier - a function that returns a new mutable container. Here we need to provide a list that should hold the results of reduction;
accumulator - is a function defines the logic on how to add stream elements into the container;
combiner - a function that defines how to merge the two containers with partial results obtained while executing the stream in parallel.
List<Integer> a = List.of(1, 2, 5);
List<Integer> b = List.of(1, 2, 8);
List<Integer> result = IntStream.range(0, Math.min(a.size(), b.size()))
.filter(i -> a.get(i) != b.get(i)) // filter out non-equal elements
.collect(
() -> Arrays.asList(0, 0), // supplier - provides a container which contain the results of reduction
(list, i) -> { // combiner - difines the logic on the container should be updated
if (a.get(i) > b.get(i)) list.set(0, list.get(0) + 1);
else list.set(1, list.get(1) + 1);
},
(left, right) -> { // accumulator - provides the logic for merging the two containers while executing in parallel
left.set(0, left.get(0) + right.get(0));
left.set(1, left.get(1) + right.get(1));
}
);
System.out.println(result);
Output:
[0, 1]
In case you doubt where it's a correct way to approach this problem with streams, have a look at the API documentation, paragraph Mutable reduction.
And even if after getting familiar with the reference above, you might think that it can be done without collect(). For instance, why don't we simply apply filter().forEach() to make the code leaner? Then have a look at another part of the API documentation, which is dedicated to Side-effects.

IntStream.range().filter.mapToObj().collect what exactly the means

I am new to Java and stuck while reading these particular lines of code:
private static void checkPrice(final List<String> prices) {
List<String> inputCountryCodes = IntStream.range(0, prices.size())
.filter(index -> (index % 2 == 0))
.mapToObj(prices::get)
.collect(Collectors.toList());
List<String> distinctInputCountryCodes = inputCountryCodes.stream()
.distinct()
.collect(Collectors.toList());
}
Can anyone please explain the para1 and para2 of the code above what these particular lines of code do here?
IntStream.range(0, prices.size())
Its generating a integer stream from 0 to size(excluding) of prize List.
So lets say your prices.size() equals 3 then InputStream will emmit 0, 1, 2
filter(index -> (index % 2 == 0))
Its intermediate operation will then filter those numbers produced by InputStream
basis on that number will be moded with 2. i.e to check if the number is even.
If it even it will be passed further or else thrown out
mapToObj(prices::get)
mapToObj It's taking that filtered number (even number) and use it to get String type object from prices.
So above code is like
mapToObj(num -> {
String item = prices.get(num);
return item;
})
collect(Collectors.toList());
collect the result from mapToObj into List<String>
So Your List<String> inputCountryCodes will contain item from prices whose index is even number.
inputCountryCodes.stream().distinct()
This will create a Stream out of your inputCountryCodes which is of type List<String>
then only take out distinct items from it.
Example if your inputCountryCodes has item1, item2, item2, item1
it will result into item1,item2
collect(Collectors.toList());
Then collect result from distinct to List<String>
So finally your List<String> distinctInputCountryCodes will contains items from prices
a) whose index is an even number
b) And items are distinct
Let's go through this one by one :
List<String> inputCountryCodes = IntStream.range(0, prices.size()).filter(index -> (index % 2 == 0))
.mapToObj(prices::get)
.collect(Collectors.toList());
IntStream is Java interface for stream specifically for int type (for stream, google stream or Java.util.stream
range(0, prices.size) is static IntStream range(int startInclusive, int endExclusive)
^ Creates an intstream from 0, prices.size() - 1
prices.size() is length of prices
filter is same in english as is in Java. It returns a stream consisting of the elements of this stream that match the given predicate (removes all those that don't match)
index -> index % 2 == 0 is lambda fn that checks whether elements are divisable by 2 or not (%2 == 0 is the check)
mapToObj eturns an object-valued Stream consisting of the results of applying the given function.
collect is a mutable reduce function
Collectors.toList() is class extending Object and toList is one of functions.
That's para 1
List<String> distinctInputCountryCodes = inputCountryCodes.stream().distinct()
.collect(Collectors.toList());
inputCountryCodes the string list obtained above
stream() turns list into a stream
distinct is name sake it gives distinct elements in a stream.
collect(Collectors.toList()) Already explained
Hope that explains it
The first one is something like the following:
List<String> result = new ArrayList<>();
for(int i = 0; i < prices.size(); i++) {
if (i % 2 == 0)
result.add(prices.get(i));
The second one iterates through the list and removes the duplicates by boxing the int to Integer and using a LinkedHashSet object under the hood.
Interpretation
You can think range(int a, int b) as a create a for loop beginning from a to b
filter(Predicate predicate) applies the given predicate on the stream and filtering accordingly. This is similar to if(condition) -> the iteration shall pass
collect(Collectors.toList())collects the iterations, which made it this far in a list object.
distinct()removes the duplicates by first boxing the primitives, passing the objects to a set object and returning the distinct result.

How to find all indexes of an object in a list without loop?

I have a problem which it probably pretty simple, but I can't find an answer for it.
I would like to be able to get all indexes where an object occurs in a list but without doing a loop over the list.
public void exampleFunction(){
ArrayList<Integer> completeList = new ArrayList<>();
completeList.add(1);
completeList.add(2);
completeList.add(1);
Integer searchObject = 1;
List<Integer> indexes = DO SOMETHING TO GET THE INDEXES LIST [0, 2];
}
You can use the Stream API, by creating an IntStream that has all the indices of the completeList, then filter out the indices where 1 is not found:
List<Integer> indexList = IntStream.range(0, completeList.size())
.filter(x -> completeList.get(x) == 1)
.collect(Collectors.toList());

Return the N last elements in Java Streams [duplicate]

This question already has answers here:
Get last n elements from stream
(4 answers)
Closed 6 years ago.
This question is just for fun.
I have this method:
private static String getBaseDomain(String fullDomain) {
// we take the base domain from the usual xxx.yyy.basedomain.tld: we
// want only the last 2 elements: basedomain.tld
List<String> elements = Arrays.asList(fullDomain.split("\\."));
if( elements.size() > 2){
elements = elements.subList(elements.size()-2, elements.size());
}
return String.join(".", elements);
}
And I'm wondering how to get the same result using the java stream API (actually, I'm wondering which method would be the most resource-efficient).
I can't figure how to get only the last 2 elements from a stream: limit(2) will give me the first two, and for skip(XXX) I don't know how to extract the size of the stream "inline".
Can you tell me how you would do?
You could use skip:
elements.stream().skip(elements.size() - 2)
From the API:
Returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream. If this stream contains fewer than n elements then an empty stream will be returned.
Probably useless example:
// a list made of a, b, c and d
List<String> l = Arrays.asList("a", "b", "c", "d");
// prints c and d
l.stream().skip(l.size() - 2).forEach(System.out::println);
Probably useless note:
As mentioned by a few, this only works if you have a size to work with, i.e. if you're streaming from a collection.
Quoting Nicolas, a stream doesn't have a size.
You could do a bit of inlining of your original approach, which I think shortens it up nicely, and you don't even have to use a stream:
String[] a = fullDomain.split("\\.");
return String.join(".", Arrays.asList(a)
.subList(Math.max(0, a.length-2), a.length));
If you really want to use a stream, you can use the array-subrange stream source:
String[] a = fullDomain.split("\\.");
return Arrays.stream(a, Math.max(0, a.length-2), a.length)
.collect(Collectors.joining("."));
If all you have is a stream, whose size you don't have in advance, I'd just dump the elements into an ArrayDeque:
final int N = 2;
Stream<String> str = ... ;
Deque<String> deque = new ArrayDeque<>(N);
str.forEachOrdered(s -> {
if (deque.size() == N) deque.removeFirst();
deque.addLast(s);
});
return String.join(".", deque);
Of course, this isn't as general as writing a collector, but for simple cases it's probably just fine.
If it is an indexed collection you could use
IntStream.range(elements.size() - 2, elements.size()).mapToObj(elements::get).forEach(System.out::print);
If elements is a stream, you can write a custom collector to keep just the last K elements (there may well be a collector like this already):
List<?> lastK = ints.stream().collect(
Collector.of(
LinkedList::new,
(listA, el) -> {
listA.add(el);
if (listA.size() > K) {
listA.remove(0);
}
},
(listA, listB) -> {
while (listB.size() < K && !listA.isEmpty()) {
listB.addFirst(listA.removeLast());
}
return listB;
}));

Find indices of common elements in arraylists in java

I have several ArrayLists with no repeated elements. I want to find their intersection and return indices of common elements in each arraylist.
For example, if I have input as {0,1,2},{3,0,4},{5,6,0}, then I want to return {0},{1},{2} i.e. indices of common element 0 here.
One way I can think of is to use succesive retainAll() on all ArrayLists to get intersection, and then finding indices of elements of intersection using indexOf() for each input ArrayList.
Is there a better way to do that ?
Sorting the list first would require at least O(nlogn) time. If you are looking for a more efficient algorithm you could get O(n) using hashmaps.
For example with
A=[0,1,2],B=[3,0,4],C=[5,6,0]
You can loop through each list and append elements with a hash on the element. The final hash will look like
H = {0:[0,1,2], 1:[1], 2:[2], 3:[0], 4:[2], 5:[0], 6:[1]}
Here, the key is the element, and the value is the index in it's corresponding list. Now, just loop through the hashmap to find any lists that have a size of 3, in this case, to get the indices.
The code would look something like this (untested):
int[][] lists = {{0,1,2}, {3,0,4}, {5,6,0}};
// Create the hashmap
Map<Integer, List<Integer>> H = new HashMap<Integer, List<Integer>>();
for(int i = 0; i < lists.length; i++){
for(int j = 0; j < lists[0].length; j++){
// create the list if this is the first occurance
if(!H.containsKey(lists[i][j]))
H.put(lists[i][j], new ArrayList<Integer>());
// add the index to the list
H.get(lists[i][j]).add(j);
}
}
// Print out indexes for elements that are shared between all lists
for(Map.Entry<Integer, List<Integer>> e : H.entrySet()){
// check that the list of indexes matches the # of lists
if(e.getValue().size() == lists.length){
System.out.println(e.getKey() + ":" + e.getValue());
}
}
EDIT: Just noticed you suggested using retainAll() in your question. That would also be O(n).
Here is a very inefficient but fairly readable solution using streams that returns you a list of lists.
int source[][];
Arrays.stream(source)
.map(list -> IntMap.range(0, list.length)
.filter(i -> Arrays.stream(source)
.allMatch(l -> Arrays.binarySearch(l, list[i]) >= 0))
.collect(Collectors.toList()))
.collect(Collectors.toList());
You can add toArray calls to convert to arrays if required.

Categories