Related
This question already has answers here:
Remove elements from collection while iterating
(9 answers)
Closed 1 year ago.
List<Integer> listArr = new ArrayList<>();
listArr.add(5);
listArr.add(7);
listArr.add(90);
listArr.add(11);
listArr.add(55);
listArr.add(60);
for(int i = 0; i < listArr.size(); i++) {
if (listArr.get(i) % 2 != 0) {
listArr.remove(i);
}
}
I'm trying to remove all odd numbers from the ArrayList and it must be for or foreEach loop. The result would be 7, 90, 55, 60 for the numbers that are remaining in the ArrayList after loop is finished. When I set a condition:
if(listArr.get(i) % 2 == 0)
Everything is working fine. All even numbers are removed, but in the first example that's not the case for odd numbers. Why is this happening?
Because when you remove a item in the arraylist, you've changed the list, try to iterate it in reverse mode.
break down:
when i = 0, your code removed it from list, then index 0 in your list is 7
when i = 1, the index on 1 is 90, so your code doesn't remove it.
when i =2, the index on 2 is 11, so your code remove it, then index on 2 in the list is 55,
when i = 3, the index on 3 i 60, so it will be kept.
End of loop, that's why your code output what you see, and that's why you should run it in reverse mode
You shouldn't edit list with for-loop(both add and remove), instead of use iterator like:
List<Integer> listArr = new ArrayList<>();
listArr.add(5);
listArr.add(7);
listArr.add(90);
listArr.add(11);
listArr.add(55);
listArr.add(60);
var it = listArr.iterator();
while (it.hasNext()) {
var n = it.next();
if (n % 2 == 0) {
it.remove();
}
}
The answer is just one simple line, indeed you can achieve it with many approaches with stream api, here we use Collector interface.
public class Main {
public static void main(String[] args) {
//question values
List<Integer> list = new ArrayList<>();
list.add(5);
list.add(7);
list.add(90);
list.add(11);
list.add(55);
list.add(60);
//answer
List<Integer> result = list.stream().collect(ArrayList::new, (u, v) -> {if(v % 2 == 0) u.add(v);}, List::addAll);
//validating result
result.forEach(System.out::println);
}
}
More over you can achive the same result with creating custom collector like below
public class OddNumbersCollector implements Collector<Integer, List<Integer>, List<Integer>> {
#Override
public Supplier<List<Integer>> supplier() {
return ArrayList::new;
}
#Override
public BiConsumer<List<Integer>, Integer> accumulator() {
return (u, v) -> {
if(v % 2 == 0)
u.add(v);
};
}
#Override
public BinaryOperator<List<Integer>> combiner() {
return (u, v) -> {
u.addAll(v);
return u;
};
}
#Override
public Function<List<Integer>, List<Integer>> finisher() {
return Function.identity();
}
#Override
public Set<Characteristics> characteristics() {
return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH, Characteristics.CONCURRENT));
}
}
And answer int this case becomes
List<Integer> result = list.stream().collect(new OddNumbersCollector());
That's all, no need to extra efforts .
I have a stream of objects like this:
"0", "1", "2", "3", "4", "5",
How can I transform it to stream of pairs :
{ new Pair("0", "1"), new Pair("2", "3"), new Pair("4", "5")}.
The stream size is unknown. I am reading data from a file that might be big. I have only iterator to collection and I transform this iterator to stream using spliterator.
I know that here is a answer for processing adjacent pairs with StreamEx :
Collect successive pairs from a stream
Can this be done in java or StreamEx ?
Thanks
It's not a natural fit but you can do
List input = ...
List<Pair> pairs = IntStream.range(0, input.size() / 2)
.map(i -> i * 2)
.mapToObj(i -> new Pair(input.get(i), input.get(i + 1)))
.collect(Collectors.toList());
To create Pairs as you go in a stream you need a stateful lambdas which should be generally avoided but can be done. Note: this will only works if the stream is single threaded. i.e. not parallel.
Stream<?> stream =
assert !stream.isParallel();
Object[] last = { null };
List<Pair> pairs = stream.map(a -> {
if (last[0] == null) {
last[0] = a;
return null;
} else {
Object t = last[0];
last[0] = null;
return new Pair(t, a);
}
}).filter(p -> p != null)
.collect(Collectors.toList());
assert last[0] == null; // to check for an even number input.
If you don't want to collect the elements
The title of the question says collect pairs from a stream, so I'd assume that you want to actually collect these, but you commented:
Your solution works, the problem is that it loads the data from file to PairList and then I may use stream from this collection to process pairs. I can't do it because the data might be too big to store in the memory.
so here's a way to do this without collecting the elements.
It's relatively straightforward to transform an Iterator<T> into an Iterator<List<T>>, and from that to transform a stream into a stream of pairs.
/**
* Returns an iterator over pairs of elements returned by the iterator.
*
* #param iterator the base iterator
* #return the paired iterator
*/
public static <T> Iterator<List<T>> paired(Iterator<T> iterator) {
return new Iterator<List<T>>() {
#Override
public boolean hasNext() {
return iterator.hasNext();
}
#Override
public List<T> next() {
T first = iterator.next();
if (iterator.hasNext()) {
return Arrays.asList(first, iterator.next());
} else {
return Arrays.asList(first);
}
}
};
}
/**
* Returns an stream of pairs of elements from a stream.
*
* #param stream the base stream
* #return the pair stream
*/
public static <T> Stream<List<T>> paired(Stream<T> stream) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(paired(stream.iterator()), Spliterator.ORDERED),
false);
}
#Test
public void iteratorAndStreamsExample() {
List<String> strings = Arrays.asList("a", "b", "c", "d", "e", "f");
Iterator<List<String>> pairs = paired(strings.iterator());
while (pairs.hasNext()) {
System.out.println(pairs.next());
// [a, b]
// [c, d]
// [e, f]
}
paired(Stream.of(1, 2, 3, 4, 5, 6, 7, 8)).forEach(System.out::println);
// [1, 2]
// [3, 4]
// [5, 6]
// [7, 8]
}
If you want to collect the elements...
I'd do this by collecting into a list, and using an AbstractList to provide a view of the elements as pairs.
First, the PairList. This is a simple AbstractList wrapper around any list that has an even number of elements. (This could easily be adapted to handle odd length lists, once the desired behavior is specified.)
/**
* A view on a list of its elements as pairs.
*
* #param <T> the element type
*/
static class PairList<T> extends AbstractList<List<T>> {
private final List<T> elements;
/**
* Creates a new pair list.
*
* #param elements the elements
*
* #throws NullPointerException if elements is null
* #throws IllegalArgumentException if the length of elements is not even
*/
public PairList(List<T> elements) {
Objects.requireNonNull(elements, "elements must not be null");
this.elements = new ArrayList<>(elements);
if (this.elements.size() % 2 != 0) {
throw new IllegalArgumentException("number of elements must have even size");
}
}
#Override
public List<T> get(int index) {
return Arrays.asList(elements.get(index), elements.get(index + 1));
}
#Override
public int size() {
return elements.size() / 2;
}
}
Then we can define the collector that we need. This is essentially shorthand for collectingAndThen(toList(), PairList::new):
/**
* Returns a collector that collects to a pair list.
*
* #return the collector
*/
public static <E> Collector<E, ?, PairList<E>> toPairList() {
return Collectors.collectingAndThen(Collectors.toList(), PairList::new);
}
Note that it could be worthwhile defining a PairList constructor that doesn't defensively copy the list, for the use case that we know the backing list is freshly generated (as in this case). That's not really essential right now, though. But once we did that, this method would be collectingAndThen(toCollection(ArrayList::new), PairList::newNonDefensivelyCopiedPairList).
And now we can use it:
/**
* Creates a pair list with collectingAndThen, toList(), and PairList::new
*/
#Test
public void example() {
List<List<Integer>> intPairs = Stream.of(1, 2, 3, 4, 5, 6)
.collect(toPairList());
System.out.println(intPairs); // [[1, 2], [2, 3], [3, 4]]
List<List<String>> stringPairs = Stream.of("a", "b", "c", "d")
.collect(toPairList());
System.out.println(stringPairs); // [[a, b], [b, c]]
}
Here's a complete source file with a runnable example (as a JUnit test):
package ex;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;
public class PairCollectors {
/**
* A view on a list of its elements as pairs.
*
* #param <T> the element type
*/
static class PairList<T> extends AbstractList<List<T>> {
private final List<T> elements;
/**
* Creates a new pair list.
*
* #param elements the elements
*
* #throws NullPointerException if elements is null
* #throws IllegalArgumentException if the length of elements is not even
*/
public PairList(List<T> elements) {
Objects.requireNonNull(elements, "elements must not be null");
this.elements = new ArrayList<>(elements);
if (this.elements.size() % 2 != 0) {
throw new IllegalArgumentException("number of elements must have even size");
}
}
#Override
public List<T> get(int index) {
return Arrays.asList(elements.get(index), elements.get(index + 1));
}
#Override
public int size() {
return elements.size() / 2;
}
}
/**
* Returns a collector that collects to a pair list.
*
* #return the collector
*/
public static <E> Collector<E, ?, PairList<E>> toPairList() {
return Collectors.collectingAndThen(Collectors.toList(), PairList::new);
}
/**
* Creates a pair list with collectingAndThen, toList(), and PairList::new
*/
#Test
public void example() {
List<List<Integer>> intPairs = Stream.of(1, 2, 3, 4, 5, 6)
.collect(toPairList());
System.out.println(intPairs); // [[1, 2], [2, 3], [3, 4]]
List<List<String>> stringPairs = Stream.of("a", "b", "c", "d")
.collect(toPairList());
System.out.println(stringPairs); // [[a, b], [b, c]]
}
}
Assuming there is a Pair with left, right and getters and a constructor:
static class Paired<T> extends AbstractSpliterator<Pair<T>> {
private List<T> list = new ArrayList<>(2);
private final Iterator<T> iter;
public Paired(Iterator<T> iter) {
super(Long.MAX_VALUE, 0);
this.iter = iter;
}
#Override
public boolean tryAdvance(Consumer<? super Pair<T>> consumer) {
getBothIfPossible(iter);
if (list.size() == 2) {
consumer.accept(new Pair<>(list.remove(0), list.remove(0)));
return true;
}
return false;
}
private void getBothIfPossible(Iterator<T> iter) {
while (iter.hasNext() && list.size() < 2) {
list.add(iter.next());
}
}
}
Usage would be:
Iterator<Integer> iterator = List.of(1, 2, 3, 4, 5).iterator();
Paired<Integer> p = new Paired<>(iterator);
StreamSupport.stream(p, false)
.forEach(pair -> System.out.println(pair.getLeft() + " " + pair.getRight()));
I know I'm late to the party, but all of the answers seem to be really complicated or have a lot of GC overhead/short-lived objects (which is not a big deal with modern JVMs), but why not do it simply like this?
public class PairCollaterTest extends TestCase {
static class PairCollater<T> implements Function<T, Stream<Pair<T, T>>> {
T prev;
#Override
public Stream<Pair<T, T>> apply(T curr) {
if (prev == null) {
prev = curr;
return Stream.empty();
}
try {
return Stream.of(Pair.of(prev, curr));
} finally {
prev = null;
}
}
}
public void testPairCollater() {
Stream.of("0", "1", "2", "3", "4", "5").sequential().flatMap(new PairCollater<>()).forEach(System.out::println);
}
}
Prints:
(0,1)
(2,3)
(4,5)
Just replace IntStream.range(1, 101) with your stream (you don't need to know your stream's size) -
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public class TestClass {
public static void main(String[] args) {
final Pair pair = new Pair();
final List<Pair> pairList = new ArrayList<>();
IntStream.range(1, 101)
.map(i -> {
if (pair.a == null) {
pair.a = i;
return 0;
} else {
pair.b = i;
return 1;
}
})
.filter(i -> i == 1)
.forEach(i -> {
pairList.add(new Pair(pair));
pair.reset();
});
pairList.stream().forEach(p -> System.out.print(p + " "));
}
static class Pair {
public Object a;
public Object b;
public Pair() {
}
public Pair(Pair orig) {
this.a = orig.a;
this.b = orig.b;
}
void reset() {
a = null;
b = null;
}
#Override
public String toString() {
return "{" + a + "," + b + '}';
}
}
}
I want to double a Stream (no DoubleStream). Meaning I start with a stream and want to get a new stream where each element of the old stream is streamed twice. So 1,2,3,4,4,5 gives us 1,1,2,2,3,3,4,4,4,4,5,5. Is there such a stream operation?
Create an inner stream which will contain current element two times and flatMap this stream.
stream.flatMap(e -> Stream.of(e,e))
If you want to multiply the number of elements by n you can create an utility method like this one:
public static <T> Stream<T> multiplyElements(Stream<T> in, int n) {
return in.flatMap(e -> IntStream.range(0, n).mapToObj(i -> e));
// we can also use IntStream.rangeClosed(1, n)
// but I am used to iterating from 0 to n (where n is excluded)
}
(but try to use a better name for this method, since the current one may be ambiguous)
Usage example:
multiplyElements(Stream.of(1,2), 3).forEach(System.out::println);
Output:
1
1
1
2
2
2
You can create a stream of 2 elements for each original element and flatMap it:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 4, 5);
List<Integer> doubled = list.stream().flatMap(i -> Stream.of(i, i)).collect(toList());
Here's a simple example of what biziclop has described in the comments.
static <E> Collection<E> multiply(Collection<E> source, int count) {
return new AbstractCollection<E>() {
#Override
public int size() {
return count * source.size();
}
#Override
public Iterator<E> iterator() {
return new Iterator<E>() {
final Iterator<E> it = source.iterator();
E next;
int i = 0;
#Override
public boolean hasNext() {
return i < size();
}
#Override
public E next() {
if (hasNext()) {
if ((i % count) == 0) {
next = it.next();
}
++i;
return next;
} else {
throw new NoSuchElementException();
}
}
};
}
};
}
(Working example on Ideone.)
CW'd since it wasn't my idea and the flatMap suggestions more directly answer the question.
Say I have several List<T>s, I will put them into another list or other collections, so I don't know how many list<T> I have until I call List<List<T>>.size()
Take below List<Integer> as an example:
list1=[1,2]
list2=[3,4]
list3=[5,6]
....
listn=[2*n-1,2n];
How can I get the result of list1*list2*list3*...listn as a Cartesian product?
For example:
list1*list2*list3
should be:
[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]
You can use recursion to achieve it, your base case of recursion is when input is empty then return empty list, else process the remaining elements. E.g.
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
public class CartesianProduct {
public static <T> List<List<T>> calculate(List<List<T>> input) {
List<List<T>> res = new ArrayList<>();
if (input.isEmpty()) { // if no more elements to process
res.add(new ArrayList<>()); // then add empty list and return
return res;
} else {
// we need to calculate the cartesian product
// of input and store it in res variable
process(input, res);
}
return res; // method completes , return result
}
private static <T> void process(List<List<T>> lists, List<List<T>> res) {
//take first element of the list
List<T> head = lists.get(0);
//invoke calculate on remaining element, here is recursion
List<List<T>> tail = calculate(lists.subList(1, lists.size()));
for (T h : head) { // for each head
for (List<T> t : tail) { //iterate over the tail
List<T> tmp = new ArrayList<>(t.size());
tmp.add(h); // add the head
tmp.addAll(t); // and current tail element
res.add(tmp);
}
}
}
public static void main(String[] args) {
//we invoke the calculate method
System.out.println(calculate(Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6))));
}
}
Output
[[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]
Thanks to #sol4me 's answer using tail recursion, here is another version which is not using tail recursion but I think is easier to understand.
public class CartesianProduct {
public static <T> List<List<T>> calculate(List<List<T>> input) {
List<List<T>> result = new ArrayList<List<T>>();
if (input.isEmpty()) { // If input an empty list
// add empty list and return
result.add(new ArrayList<T>());
return result;
} else {
// get the first list as a head
List<T> head = input.get(0);
// recursion to calculate a tail list
List<List<T>> tail = calculate(input.subList(1, input.size()));
// we merge every head element with every tail list.
for (T h : head) {
for (List<T> t : tail) {
List<T> resultElement = new ArrayList<T>();
resultElement.add(h);
resultElement.addAll(t);
result.add(resultElement);
}
}
}
return result;
}
public static void main(String[] args) {
List<List<Integer>> bigList = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6),
Arrays.asList(7, 8));
System.out.println(calculate(bigList));
}
}
The map-and-reduce approach using nested loops
Prepare a list of lists List<List<T>> populated with a single empty value. This list is used further as a storage of intermediate results and as a final result.
Sequentially append the data from incoming lists List<List<T>> to the intermediate result and obtain the final result. Schematically, this iterative process looks as follows:
result0: [[]]
list1: [1,2]
-------
result1: [[1],[2]]
list2: [3,4]
-------
result2: [[1,3],[1,4],[2,3],[2,4]]
list3: [5,6]
-------
result3: [[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]
Try it online!
/**
* #param lists an arbitrary number of lists
* #param <T> the type of the elements
* #return the Cartesian product
*/
public static <T> List<List<T>> cartesianProduct(List<List<T>> lists) {
// check if incoming data is not null
if (lists == null) return Collections.emptyList();
// Cartesian product, intermediate result
List<List<T>> cp = Collections.singletonList(Collections.emptyList());
// iterate through incoming lists
for (List<T> list : lists) {
// non-null and non-empty lists
if (list == null || list.size() == 0) continue;
// intermediate result for next iteration
List<List<T>> next = new ArrayList<>();
// rows of current intermediate result
for (List<T> row : cp) {
// elements of current list
for (T el : list) {
// new row for next intermediate result
List<T> nRow = new ArrayList<>(row);
nRow.add(el);
next.add(nRow);
}
}
// pass to next iteration
cp = next;
}
// Cartesian product, final result
return cp;
}
public static void main(String[] args) {
List<List<Integer>> lists = prepareLists(3);
List<List<Integer>> cp = cartesianProduct(lists);
// output without spaces
System.out.println(lists.toString().replace(" ", ""));
System.out.println(cp.toString().replace(" ", ""));
}
// supplementary method, prepares lists for multiplication
public static List<List<Integer>> prepareLists(int n) {
List<List<Integer>> lists = new ArrayList<>(n);
for (int i = 1; i <= n; i++)
lists.add(Arrays.asList(i * 2 - 1, i * 2));
return lists;
}
Output:
[[1,2],[3,4],[5,6]]
[[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]
See also: Generate all combinations from multiple lists
This is my first time working with linked lists and I've created both of my lists from two separate files and tokenized the data.
Now I need to compare the data and look for similarities, by that I mean the same data.
Such has list 1: 1, 3, dog and list 2: 1, dog, cat. Both have 1 and dog.
I am unsure how to approach this, and would love some tips, input, or hints.
The code which compares two Lists only in case when their elements are comparable (i.e. implements Comparable interface).
This is parametrized so you can provide any type of List element which implements Comparable (<T extends Comparable<T>>)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ComparatorTest {
public static void main(String[] args) {
List<Integer> list1 = new ArrayList<>(Arrays.asList(new Integer[]{1, 2, 3, 6, 7}));
List<Integer> list2 = new ArrayList<>(Arrays.asList(new Integer[]{1, 2, 3, 6}));
final int result = new ComparatorTest().compareTo(list1, list2);
System.out.println("result = " + result);
}
<T extends Comparable<T>> int compareTo(List<T> list1, List<T> list2) {
int minLength = Math.min(list1.size(), list2.size());
for (int i = 0; i < minLength; i++) {
final int compareValue = list1.get(i).compareTo(list2.get(i));
if (compareValue != 0) {
return compareValue; // They are already not equal
}
}
if (list1.size() == list2.size()) {
return 0; // They are equal
} else if (list1.size() < list2.size()) {
return -1; // list 1 is smaller
} else {
return 1;
}
}
}