Anyone knows how to achieve following piece code in a Java 8 way respectively is there any stream methods to detect the first element in a forEach?
List<String> myList = new ArrayList<String>();
myList.add("A");
myList.add("B");
int i = 0;
for (final String value : myList) {
if (i == 0) {
System.out.println("Hey that's the first element");
}
System.out.println(value);
i++;
}
Java8:
myList.stream().forEach(value -> {
// TODO: How to do something special for first element?
System.out.println(value);
});
Furthermore, let's says that the goal is the following (console output):
A Something special
B
C
D
E
F
May this work for you?
myList.stream().findFirst().ifPresent(e -> System.out.println("Special: " + e));
myList.stream().skip(1).forEach(System.out::println);
Output:
Special: A
B
Alternative:
myList.stream().findFirst().ifPresent(e -> somethingSpecial(e));
myList.stream().forEach(System.out::println);
I don't think there is something provided in stream.
There are several alternatives you may consider:
Method 1:
int[] i = {0}; // I am using it only as an int holder.
myList.stream().forEach(value -> {
if (i[0]++ == 0) {
doSomethingSpecial;
}
System.out.println(value);
});
Method 2:
If your list is quick for random access (e.g. ArrayList), you may consider:
IntStream.range(0, myList.size())
.forEach(i -> {
ValueType value = myList.get(i);
if (i == 0) {
doSomethingSpecial();
}
System.out.println(value);
});
Try StreamEx
StreamEx.of("A", "B", "C") //
.peekFirst(e -> System.out.println(e + " Something special")).skip(1) //
.forEach(System.out::println);
If you were trying to achieve it in a single stream statement, here's how I'd do it:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class Solution {
public static void main(String[] args) {
List<String> myList = new ArrayList<>();
myList.add("A");
myList.add("B");
myList.add("C");
myList.add("D");
myList.add("E");
myList.add("F");
AtomicBoolean firstElementProcessed = new AtomicBoolean(false);
myList.stream().map(s -> {
if (firstElementProcessed.getAndSet(true)) {
return s;
}
return (s + " Something special");
}).forEach(System.out::println);
}
}
And maybe for better readability, I'd refactor it to:
public class Solution {
...
AtomicBoolean firstElementProcessed = new AtomicBoolean(false);
myList.stream().map(s -> getString(firstElementProcessed, s)).forEach(System.out::println);
}
private static String getString(AtomicBoolean firstElementProcessed, String s) {
if (firstElementProcessed.getAndSet(true)) {
return s;
}
return (s + " Something special");
}
}
Something like
List<String> mylist = Arrays.asList("A", "B", "C", "D", "E", "F");
Optional<String> findFirst = mylist.stream().findFirst();
findFirst.ifPresent(i -> System.out.println("Hey! " + i + ", that's the first element"));
mylist.stream().skip(1).forEach(System.out::println);
There is no reason to not use simple for loop, foreach is is pretty but limited to side effects.
Related
How can I perform multiple unrelated operations on elements of a single stream?
Say I have a List<String> composed from a text. Each string in the list may or may not contain a certain word, which represents an action to perform. Let's say that:
if the string contains 'of', all the words in that string must be counted
if the string contains 'for', the portion after the first occurrence of 'for' must be returned, yielding a List<String> with all substrings
Of course, I could do something like this:
List<String> strs = ...;
List<Integer> wordsInStr = strs.stream()
.filter(t -> t.contains("of"))
.map(t -> t.split(" ").length)
.collect(Collectors.toList());
List<String> linePortionAfterFor = strs.stream()
.filter(t -> t.contains("for"))
.map(t -> t.substring(t.indexOf("for")))
.collect(Collectors.toList());
but then the list would be traversed twice, which could result in a performance penalty if strs contained lots of elements.
Is it possible to somehow execute those two operations without traversing twice over the list?
If you want a single pass Stream then you have to use a custom Collector (parallelization possible).
class Splitter {
public List<String> words = new ArrayList<>();
public List<Integer> counts = new ArrayList<>();
public void accept(String s) {
if(s.contains("of")) {
counts.add(s.split(" ").length);
} else if(s.contains("for")) {
words.add(s.substring(s.indexOf("for")));
}
}
public Splitter merge(Splitter other) {
words.addAll(other.words);
counts.addAll(other.counts);
return this;
}
}
Splitter collect = strs.stream().collect(
Collector.of(Splitter::new, Splitter::accept, Splitter::merge)
);
System.out.println(collect.counts);
System.out.println(collect.words);
Here is the answer to address the OP from a different aspect. First of all, let's take a look how fast/slow to iterate a list/collection. Here is the test result on my machine by the below performance test:
When: length of string list = 100, Thread number = 1, loops = 1000, unit = milliseconds
OP: 0.013
Accepted answer: 0.020
By the counter function: 0.010
When: length of string list = 1000_000, Thread number = 1, loops = 100, unit = milliseconds
OP: 99.387
Accepted answer: 89.848
By the counter function: 59.183
Conclusion: The percentage of performance improvement is pretty small or even slower(if the length of string list is small). generally, it's a mistake to reduce the iteration of list/collection which is loaded in memory by the more complicate collector. you won't get much performance improvements. we should look into somewhere else if there is a performance issue.
Here is my performance test code with tool Profiler: (I'm not going to discuss how to do a performance test here. if you doubt the test result, you can do it again with any tool you believe in)
#Test
public void test_46539786() {
final int strsLength = 1000_000;
final int threadNum = 1;
final int loops = 100;
final int rounds = 3;
final List<String> strs = IntStream.range(0, strsLength).mapToObj(i -> i % 2 == 0 ? i + " of " + i : i + " for " + i).toList();
Profiler.run(threadNum, loops, rounds, "OP", () -> {
List<Integer> wordsInStr = strs.stream().filter(t -> t.contains("of")).map(t -> t.split(" ").length).collect(Collectors.toList());
List<String> linePortionAfterFor = strs.stream().filter(t -> t.contains("for")).map(t -> t.substring(t.indexOf("for")))
.collect(Collectors.toList());
assertTrue(wordsInStr.size() == linePortionAfterFor.size());
}).printResult();
Profiler.run(threadNum, loops, rounds, "Accepted answer", () -> {
Splitter collect = strs.stream().collect(Collector.of(Splitter::new, Splitter::accept, Splitter::merge));
assertTrue(collect.counts.size() == collect.words.size());
}).printResult();
final Function<String, Integer> counter = s -> {
int count = 0;
for (int i = 0, len = s.length(); i < len; i++) {
if (s.charAt(i) == ' ') {
count++;
}
}
return count;
};
Profiler.run(threadNum, loops, rounds, "By the counter function", () -> {
List<Integer> wordsInStr = strs.stream().filter(t -> t.contains("of")).map(counter).collect(Collectors.toList());
List<String> linePortionAfterFor = strs.stream().filter(t -> t.contains("for")).map(t -> t.substring(t.indexOf("for")))
.collect(Collectors.toList());
assertTrue(wordsInStr.size() == linePortionAfterFor.size());
}).printResult();
}
You could use a custom collector for that and iterate only once:
private static <T, R> Collector<String, ?, Pair<List<String>, List<Long>>> multiple() {
class Acc {
List<String> strings = new ArrayList<>();
List<Long> longs = new ArrayList<>();
void add(String elem) {
if (elem.contains("of")) {
long howMany = Arrays.stream(elem.split(" ")).count();
longs.add(howMany);
}
if (elem.contains("for")) {
String result = elem.substring(elem.indexOf("for"));
strings.add(result);
}
}
Acc merge(Acc right) {
longs.addAll(right.longs);
strings.addAll(right.strings);
return this;
}
public Pair<List<String>, List<Long>> finisher() {
return Pair.of(strings, longs);
}
}
return Collector.of(Acc::new, Acc::add, Acc::merge, Acc::finisher);
}
Usage would be:
Pair<List<String>, List<Long>> pair = Stream.of("t of r m", "t of r m", "nice for nice nice again")
.collect(multiple());
If you want to have 1 stream through a list, you need a way to manage 2 different states, you can do this by implementing Consumer to new class.
class WordsInStr implements Consumer<String> {
ArrayList<Integer> list = new ArrayList<>();
#Override
public void accept(String s) {
Stream.of(s).filter(t -> t.contains("of")) //probably would be faster without stream here
.map(t -> t.split(" ").length)
.forEach(list::add);
}
}
class LinePortionAfterFor implements Consumer<String> {
ArrayList<String> list = new ArrayList<>();
#Override
public void accept(String s) {
Stream.of(s) //probably would be faster without stream here
.filter(t -> t.contains("for"))
.map(t -> t.substring(t.indexOf("for")))
.forEach(list::add);
}
}
WordsInStr w = new WordsInStr();
LinePortionAfterFor l = new LinePortionAfterFor();
strs.stream()//stream not needed here
.forEach(w.andThen(l));
System.out.println(w.list);
System.out.println(l.list);
I have a collection with structure like this:
#Entity
public class RRR{
private Map<XClas, YClas> xySets;
}
and XClas has a field called ZZZ
my question is:
I would like to aggregate it with lambda to get a Map<ZZZ, List<RRR>>.
Is it possible? Now I'm stuck with:
Map xxx = rrrList.stream().collect(
Collectors.groupingBy(x->x.xySets().entrySet().stream().collect(
Collectors.groupingBy(y->y.getKey().getZZZ()))));
but it's Map<Map<ZZZ, List<XClas>>, List<RRR>> so it's not what I was looking for :)
Right now just to make it work, I did aggregation with two nested loops, but it would be so great, if you could help me make it done with lambdas.
EDIT
I post what I got by now, as asked.
I already left nested loops, and I manage to work my way up to this point:
Map<ZZZ, List<RRR>> temp;
rrrList.stream().forEach(x -> x.getxySetsAsList().stream().forEach(z -> {
if (temp.containsKey(z.getKey().getZZZ())){
List<RRR> uuu = new LinkedList<>(temp.get(z.getKey().getZZZ()));
uuu.add(x);
temp.put(z.getKey().getZZZ(), uuu);
} else {
temp.put(z.getKey().getZZZ(), Collections.singletonList(x));
}
}));
Thanks in advance
Something like that? :
Map<ZZZ, List<RRR>> map = new HashMap<>();
list.stream().forEach(rrr -> {
rrr.xySets.keySet().stream().forEach(xclas -> {
if (!map.containsKey(xclas.zzz))
map.put(xclas.zzz, new ArrayList<RRR>());
map.get(xclas.zzz).add(rrr);
});
});
Another way you could do this:
Map<Z, List<R>> map = rs.stream()
.map(r -> r.xys.keySet()
.stream()
.collect(Collectors.<X, Z, R>toMap(x -> x.z, x -> r, (a, b) -> a)))
.map(Map::entrySet)
.flatMap(Collection::stream)
.collect(Collectors.groupingBy(Entry::getKey,
Collectors.mapping(Entry::getValue, Collectors.toList())));
I have tried around a bit and found the following solution, posting it here just as another example:
rrrList.stream().map(x -> x.xySets).map(Map::entrySet).flatMap(x -> x.stream())
.collect(Collectors.groupingBy(x -> x.getKey().getZZZ(),
Collectors.mapping(Entry::getValue, Collectors.toList())));
The first line could also be written as rrrList.stream().flatMap(x -> x.xySets.entrySet().stream()) which might be found more readable.
Here is self-contained example code for those wanting to play around themselves:
public static void main(String[] args) {
List<RRR> rrrList = Arrays.asList(new RRR(), new RRR(), new RRR());
System.out.println(rrrList);
Stream<Entry<XClas, YClas>> sf = rrrList.stream().map(x -> x.xySets).map(Map::entrySet).flatMap(x -> x.stream());
Map<ZZZ, List<YClas>> res = sf.collect(Collectors.groupingBy(x -> x.getKey().getZZZ(), Collectors.mapping(Entry::getValue, Collectors.toList())));
System.out.println(res);
}
public static class RRR {
static XClas shared = new XClas();
private Map<XClas, YClas> xySets = new HashMap<>();
RRR() { xySets.put(shared, new YClas()); xySets.put(new XClas(), new YClas()); }
static int s = 0; int n = s++;
public String toString() { return "RRR" + n + "(" + xySets + ")"; }
}
public static class XClas {
private ZZZ zzz = new ZZZ();
public ZZZ getZZZ() { return zzz; }
public String toString() { return "XClas(" + zzz + ")"; }
public boolean equals(Object o) { return (o instanceof XClas) && ((XClas)o).zzz.equals(zzz); }
public int hashCode() { return zzz.hashCode(); }
}
public static class YClas {
static int s = 0; int n = s++;
public String toString() { return "YClas" + n; }
}
public static class ZZZ {
static int s = 0; int n = s++ / 2;
public String toString() { return "ZZZ" + n; }
public boolean equals(Object o) { return (o instanceof ZZZ) && ((ZZZ)o).n == n; }
public int hashCode() { return n; }
}
I have the following Set, I would like to replace any instance that is multiple of 10 by the string "10". Can anyone guide me in the right direction please.
Set<Integer> set3 = new HashSet<Integer>();
for(int i = 0; i<10; i++){
Random ran = new Random();
number = 1+ran.nextInt(1000);
set3.add(number);
}
You can try this
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.stream.Stream;
public class ReplaceIntegers {
public static void main(String[] args) {
Set<Integer> set3 = new HashSet<>();
Set<Object> objectSet = new HashSet<>();
Random generator = new Random();
for(int i = 0; i < 1000; i++) {
set3.add(1+generator.nextInt(1000));
}
set3.stream()
.filter(n -> n%10 == 0)
.forEach(n -> objectSet.add(n.toString()));
objectSet.stream()
.forEach(v -> System.out.println(v));
for(Integer i : set3) {
if(i%10 == 0)
System.out.println(i + " is a multiple of 10");
else
System.out.println("Number: " + i);
}
}
}
Patrick
The output is going to be a mixed set of integers and strings, so the method signature you're writing is going to look like:
Set<Object> foo(Set<Integer> input);
First let's write the algorithm the easy way, in Scala, then we'll convert it to Java. You want to change each item in the collection, so that's a map operation.
def foo(s: Set[Int]): Set[Any] = s map { i => if (i % 10 == 0) "10" else i }
In Java 8, it's similar, but you have to convert the Set to a Stream to do the mapping, and then back to a Set again.
static Set<Object> foo(Set<Integer> s) {
return s.stream()
.map(i -> i % 10 == 0 ? "10" : i)
.collect(Collectors.toSet());
}
If you want to go back to Java 7, you don't even have streams, or lambdas to make defining the map operation feasible, so you just have to understand how map is defined and then implement it procedurally in your code.
static Set<Object> foo(Set<Integer> s) {
Set<Object> result = new HashSet<>();
for (Integer i : s) {
result.add(i % 10 == 0 ? "10" : i);
}
return result;
}
The Java 5-6 solution is almost the same, just without the diamond syntax:
static Set<Object> foo(Set<Integer> s) {
Set<Object> result = new HashSet<Object>();
for (Integer i : s) {
result.add(i % 10 == 0 ? "10" : i);
}
return result;
}
And in Java 3-4 you lose the for loop, autounboxing, and generics...
static Set foo(Set s) {
Set result = new HashSet();
Iterator it = s.iterator();
while (it.hasNext()) {
Integer i = (Integer) it.next();
Object o = i;
if (i.intValue() % 10 == 0) o = "10";
result.add(o);
}
return result;
}
Set<Integer> set3 = new HashSet<Integer>();
Set<Object> set4 = new HashSet<Object>();
for(Integer integer: set3){
if(integer%10==0){
set4.add(integer.toString());
} else {
set4.add(integer);
}
};
set3=set4;
You are unable to put String into Set<Integer>, you should decrease the specificity of the type to the closest common parent class of both String and Integer - Object. In addition, I would strongly discourage using "replace" (remove+add) on a Set you are iterating through: this can lead to potential problems with data consistency. Just copy your elements in another Set and then replace the original one.
This is a pice of my code :
ArrayList<String> Alist= new ArrayList<String>();
ArrayList<String> Blist= new ArrayList<String>();
Alist.add("gsm");
Alist.add("tablet");
Alist.add("pc");
Alist.add("mouse");
Blist.add("gsm");
Blist.add("something");
Blist.add("pc");
Blist.add("something");
so i have two array list i want to compare all items and check if they are not equal and if they are to print out only the items that are not equal.
so i make something like this:
http://postimage.org/image/adxix2i13/
sorry for the image but i have somekind of bug when i post here a for looop.
and the result is :
not equals..:tablet
not equals..:pc
not equals..:mouse
not equals..:gsm
not equals..:tablet
not equals..:pc
not equals..:mouse
not equals..:gsm
not equals..:tablet
not equals..:pc
not equals..:mouse
not equals..:gsm
not equals..:tablet
i want to print only the 2 that are not equal in the example they are gsm and pc
not equals..:gsm
not equals..:pc
Don't use != to compare strings. Use the equals method :
if (! Blist.get(i).equals(Alist.get(j))
But this wouldn't probably fix your algorithmic problem (which isn't clear at all).
If what you want is know what items are the same at the same position, you could use a simple loop :
int sizeOfTheShortestList = Math.min(Alist.size(), Blist.size());
for (int i=0; i<sizeOfTheShortestList; i++) {
if (Blist.get(i).equals(Alist.get(i))) {
System.out.println("Equals..: " + Blist.get(i));
}
}
If you want to get items that are in both lists, use
for (int i = 0; i < Alist.size(); i++) {
if (Blist.contains(Alist.get(i))) {
System.out.println("Equals..: " + Alist.get(i));
}
}
You can use the RemoveAll(Collection c) on one of the lists, if you happen to know if one list always contains them all.
You could use the following code:
ArrayList<String> Alist = new ArrayList<String>();
ArrayList<String> Blist = new ArrayList<String>();
Alist.add("gsm");
Alist.add("tablet");
Alist.add("pc");
Alist.add("mouse");
Blist.add("gsm");
Blist.add("something");
Blist.add("pc");
Blist.add("something");
for (String a : Alist)
{
for (String b : Blist)
{
if (a.equals(b))
{
System.out.println("Equals " + a);
break;
}
}
}
Output is:
Equals gsm
Equals pc
right now your comparing each element to all of the other ones. Do something like
for (int i = 0; i < Alist.size(); i++) {
if (!Alist.get(i).equals(Blist.get(i)) {
// print what you want
}
}
Thats of course assuming both lists have the same length.
Rather than writing code to manually compare list elements you might consider using Apache Commons Collections.
import org.apache.commons.collections.CollectionUtils;
List listA = ...;
List listB = ...;
Collection intersection = CollectionUtils.intersection(listA, listB);
import java.util.HashSet;
public class CheckSet<T> extends HashSet<T>{
#Override
public boolean add(T e) {
if (contains(e)) {
remove(e);
return true;
} else {
return super.add(e);
}
}
}
Add all elements of both of your lists to a CheckSet intance, and at the end it will only contain the ones not equal.
Here is one way:
public static boolean compare(List<String> first, List<String> second) {
if (first==null && second==null) return true;
if (first!=null && second==null) return false;
if (first==null && second!=null) return false;
if ( first.size()!=second.size() ) return false;
HashMap<String, String> map = new HashMap<String, String>();
for (String str : first) {
map.put(str, str);
}
for (String str : second) {
if ( ! map.containsKey(str) ) {
return false;
}
}
return true;
}
public static void main(String args[] ) throws Exception {
List<String> arrayList1 = new ArrayList<String>();
arrayList1.add("a");
arrayList1.add("b");
arrayList1.add("c");
arrayList1.add("d");
List<String> arrayList2 = new ArrayList<String>();
arrayList2.add("a");
arrayList2.add("b");
arrayList2.add("c");
arrayList2.add("d");
boolean isEqual = false;
if(arrayList1.size() == arrayList2.size()){
List<String> arrayListTemp = new ArrayList<String>();
arrayListTemp.addAll(arrayList1);
arrayListTemp.addAll(arrayList2);
HashSet<Object> hashSet = new HashSet<Object>();
hashSet.addAll(arrayListTemp);
if(hashSet.size() == arrayList1.size() &&
hashSet.size() == arrayList2.size()){
isEqual = true;
}
}
System.out.println(isEqual);
}
we can compare two different size arrayList in java or Android as follow.
ArrayList<String> array1 = new ArrayList<String>();
ArrayList<String> array2 = new ArrayList<String>();
array1.add("1");
array1.add("2");
array1.add("3");
array1.add("4");
array1.add("5");
array1.add("6");
array1.add("7");
array1.add("8");
array2.add("1");
array2.add("2");
array2.add("3");
array2.add("4");
for (int i = 0; i < array1.size(); i++) {
for (int j=0;j<array2.size();j++) {
if (array1.get(i) == array2.get(j)) {
//if match do the needful
} else {
// if not match
}
}
}
import java.util.Arrays;
public class ExampleContains {
public static boolean EligibleState(String state){
String[] cities = new String[]{"Washington", "London", "Paris", "NewYork"};
boolean test = Arrays.asList(cities).contains(state)?true:false;
return test;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(EligibleState("London"));
}
}
In Python, the enumerate function allows you to iterate over a sequence of (index, value) pairs. For example:
>>> numbers = ["zero", "one", "two"]
>>> for i, s in enumerate(numbers):
... print i, s
...
0 zero
1 one
2 two
Is there any way of doing this in Java?
For collections that implement the List interface, you can call the listIterator() method to get a ListIterator. The iterator has (amongst others) two methods - nextIndex(), to get the index; and next(), to get the value (like other iterators).
So a Java equivalent of the Python above might be:
import java.util.ListIterator;
import java.util.List;
List<String> numbers = Arrays.asList("zero", "one", "two");
ListIterator<String> it = numbers.listIterator();
while (it.hasNext()) {
System.out.println(it.nextIndex() + " " + it.next());
}
which, like the Python, outputs:
0 zero
1 one
2 two
I find this to be the most similar to the python approach.
Usage
public static void main(String [] args) {
List<String> strings = Arrays.asList("zero", "one", "two");
for(EnumeratedItem<String> stringItem : ListUtils.enumerate(strings)) {
System.out.println(stringItem.index + " " + stringItem.item);
}
System.out.println();
for(EnumeratedItem<String> stringItem : ListUtils.enumerate(strings, 3)) {
System.out.println(stringItem.index + " " + stringItem.item);
}
}
Output
0 zero
1 one
2 two
3 zero
4 one
5 two
Features
Works on any iterable
Does not create an in-memory list copy (suitable for large lists)
Supports native for each syntax
Accepts a start parameter which can be added to the index
Implementation
import java.util.Iterator;
public class ListUtils {
public static class EnumeratedItem<T> {
public T item;
public int index;
private EnumeratedItem(T item, int index) {
this.item = item;
this.index = index;
}
}
private static class ListEnumerator<T> implements Iterable<EnumeratedItem<T>> {
private Iterable<T> target;
private int start;
public ListEnumerator(Iterable<T> target, int start) {
this.target = target;
this.start = start;
}
#Override
public Iterator<EnumeratedItem<T>> iterator() {
final Iterator<T> targetIterator = target.iterator();
return new Iterator<EnumeratedItem<T>>() {
int index = start;
#Override
public boolean hasNext() {
return targetIterator.hasNext();
}
#Override
public EnumeratedItem<T> next() {
EnumeratedItem<T> nextIndexedItem = new EnumeratedItem<T>(targetIterator.next(), index);
index++;
return nextIndexedItem;
}
};
}
}
public static <T> Iterable<EnumeratedItem<T>> enumerate(Iterable<T> iterable, int start) {
return new ListEnumerator<T>(iterable, start);
}
public static <T> Iterable<EnumeratedItem<T>> enumerate(Iterable<T> iterable) {
return enumerate(iterable, 0);
}
}
Strictly speaking, no, as the enumerate() function in Python returns a list of tuples, and tuples do not exist in Java.
If however, all you're interested in is printing out an index and a value, then you can follow the suggestion from Richard Fearn & use nextIndex() and next() on an iterator.
Note as well that enumerate() can be defined using the more general zip() function (using Python syntax):
mylist = list("abcd")
zip(range(len(mylist)), mylist)
gives [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]
If you define your own Tuple class (see Using Pairs or 2-tuples in Java as a starting point), then you could certainly easily write your own zip() function in Java to make use of it (using the Tuple class defined in the link):
public static <X,Y> List<Tuple<X,Y>> zip(List<X> list_a, List<Y> list_b) {
Iterator<X> xiter = list_a.iterator();
Iterator<Y> yiter = list_b.iterator();
List<Tuple<X,Y>> result = new LinkedList<Tuple<X,Y>>();
while (xiter.hasNext() && yiter.hasNext()) {
result.add(new Tuple<X,Y>(xiter.next(), yiter.next()));
}
return result;
}
And once you have zip(), implementing enumerate() is trivial.
Edit: slow day at work, so to finish it off:
public static <X> List<Tuple<Integer,X>> enumerate (List<X> list_in) {
List<Integer> nums = new ArrayList<Integer>(list_in.size());
for (int x = 0; x < list_in.size(); x++) {
nums.add(Integer.valueOf(x));
}
return zip (nums, list_in);
}
Edit 2: as pointed out in the comments to this question, this is not entirely equivalent. While it produces the same values as Python's enumerate, it doesn't do so in the same generative fashion that Python's enumerate does. Thus for large collections this approach could be quite prohibitive.
Simple and straightforward
public static <T> void enumerate(Iterable<T> iterable, java.util.function.ObjIntConsumer<T> consumer) {
int i = 0;
for(T object : iterable) {
consumer.accept(object, i);
i++;
}
}
Sample usage:
void testEnumerate() {
List<String> strings = Arrays.asList("foo", "bar", "baz");
enumerate(strings, (str, i) -> {
System.out.println(String.format("Index:%d String:%s", i, str));
});
}
According to the Python docs (here), this is the closest you can get with Java, and it's no more verbose:
String[] numbers = {"zero", "one", "two"}
for (int i = 0; i < numbers.length; i++) // Note that length is a property of an array, not a function (hence the lack of () )
System.out.println(i + " " + numbers[i]);
}
If you need to use the List class...
List<String> numbers = Arrays.asList("zero", "one", "two");
for (int i = 0; i < numbers.size(); i++) {
System.out.println(i + " " + numbers.get(i));
}
*NOTE: if you need to modify the list as you're traversing it, you'll need to use the Iterator object, as it has the ability to modify the list without raising a ConcurrentModificationException.
Now with Java 8s Stream API together with the small ProtonPack library providing StreamUtils it can be achieved easily.
The first example uses the same for-each notation as in the question:
Stream<String> numbers = Arrays.stream("zero one two".split(" "));
List<Indexed<String>> indexedNumbers = StreamUtils.zipWithIndex(numbers)
.collect(Collectors.toList());
for (Indexed<String> indexed : indexedNumbers) {
System.out.println(indexed.getIndex() + " " + indexed.getValue());
}
Above although does not provide the lazy evaluation as in Python.
For that you must use the forEach() Stream API method:
Stream<String> numbers = Arrays.stream("zero one two".split(" "));
StreamUtils.zipWithIndex(numbers)
.forEach(n -> System.out.println(n.getIndex() + " " + n.getValue()));
The lazy evaluation can be verified with the following infinite stream:
Stream<Integer> infStream = Stream.iterate(0, i -> i++);
StreamUtils.zipWithIndex(infStream)
.limit(196)
.forEach(n -> System.out.println(n.getIndex() + " " + n.getValue()));
No. Maybe there are some libraries for supporting such a functionality. But if you resort to the standard libraries it is your job to count.
List<String> list = { "foo", "bar", "foobar"};
int i = 0;
for (String str : list){
System.out.println(i++ + str );
}
I think this should be the java functionality that resemble the python "enumerate" most, though it is quite complicated and inefficent. Basically, just map the list's indices to its elements, using ListIterator or Collector:
List<String> list = new LinkedList<>(Arrays.asList("one", "two", "three", "four"));
Map<Integer, String> enumeration = new Map<>();
ListIterator iter = list.listIterator();
while(iter.hasNext){
map.put(iter.nextIndex(), iter.next());
}
or using lambda expression:
Set<Integer, String> enumeration = IntStream.range(0, list.size()).boxed.collect(Collectors.toMap(index -> index, index -> list.get(index)));
then you can use it with an enhanced for loop:
for (Map.Entry<Integer, String> entry : enumeration.entrySet){
System.out.println(entry.getKey() + "\t" + entry.getValue());
}
By combining generics with anonymous interfaces, you can essentially create a factory method for handing enumeration. The Enumerator callback hides the messiness of the iterator underneath.
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
public class ListUtils2 {
public static interface Enumerator<T> {
void execute(int index, T value);
};
public static final <T> void enumerate(final List<T> list,
final Enumerator<T> enumerator) {
for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
enumerator.execute(it.nextIndex(), it.next());
}
}
public static final void enumerate(final String[] arr,
final Enumerator<String> enumerator) {
enumerate(Arrays.asList(arr), enumerator);
}
public static void main(String[] args) {
String[] names = { "John", "Paul", "George", "Ringo" };
enumerate(names, new Enumerator<String>() {
#Override
public void execute(int index, String value) {
System.out.printf("[%d] %s%n", index, value);
}
});
}
}
Result
[0] John
[1] Paul
[2] George
[3] Ringo
Extended Thoughts
Map, Reduce, Filter
I have taken this a step further and created map, reduce, and filter functions based on this concept.
Both Google's Guava and Apache common-collections dependencies include similar functionality. You can check them out as you wish.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
public class ListUtils {
// =========================================================================
// Enumerate
// =========================================================================
public static abstract interface Enumerator<T> {
void execute(int index, T value, List<T> list);
};
public static final <T> void enumerate(final List<T> list,
final Enumerator<T> enumerator) {
for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
enumerator.execute(it.nextIndex(), it.next(), list);
}
}
// =========================================================================
// Map
// =========================================================================
public static interface Transformer<T, U> {
U execute(int index, T value, List<T> list);
};
public static final <T, U> List<U> transform(final List<T> list,
final Transformer<T, U> transformer) {
List<U> result = new ArrayList<U>();
for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
result.add(transformer.execute(it.nextIndex(), it.next(), list));
}
return result;
}
// =========================================================================
// Reduce
// =========================================================================
public static interface Reducer<T, U> {
U execute(int index, T value, U result, List<T> list);
};
public static final <T, U> U reduce(final List<T> list,
final Reducer<T, U> enumerator, U result) {
for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
result = enumerator.execute(it.nextIndex(), it.next(), result, list);
}
return result;
}
// =========================================================================
// Filter
// =========================================================================
public static interface Predicate<T> {
boolean execute(int index, T value, List<T> list);
};
public static final <T> List<T> filter(final List<T> list,
final Predicate<T> predicate) {
List<T> result = new ArrayList<T>();
for (ListIterator<T> it = list.listIterator(); it.hasNext();) {
int index = it.nextIndex();
T value = it.next();
if (predicate.execute(index, value, list)) {
result.add(value);
}
}
return result;
}
// =========================================================================
// Predefined Methods
// =========================================================================
// Enumerate
public static <T> String printTuples(List<T> list) {
StringBuffer buff = new StringBuffer();
enumerate(list, new Enumerator<T>() {
#Override
public void execute(int index, T value, List<T> list) {
buff.append('(').append(index).append(", ")
.append(value).append(')');
if (index < list.size() - 1) {
buff.append(", ");
}
}
});
return buff.toString();
}
// Map
public static List<String> intToHex(List<Integer> list) {
return transform(list, new Transformer<Integer, String>() {
#Override
public String execute(int index, Integer value, List<Integer> list) {
return String.format("0x%02X", value);
}
});
}
// Reduce
public static Integer sum(List<Integer> list) {
return reduce(list, new Reducer<Integer, Integer>() {
#Override
public Integer execute(int index, Integer value, Integer result,
List<Integer> list) {
return result + value;
}
}, 0);
}
// Filter
public static List<Integer> evenNumbers(List<Integer> list) {
return filter(list, new Predicate<Integer>() {
#Override
public boolean execute(int index, Integer value, List<Integer> list) {
return value % 2 == 0;
}
});
}
// =========================================================================
// Driver
// =========================================================================
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(8, 6, 7, 5, 3, 0, 9);
// Enumerate
System.out.printf("%-10s: %s%n", "Enumerate", printTuples(numbers));
// Map
System.out.printf("%-10s: %s%n", "Map", intToHex(numbers));
// Reduce
System.out.printf("%-10s: %d%n", "Reduce", sum(numbers));
// Filter
System.out.printf("%-10s: %s%n", "Filter", evenNumbers(numbers));
}
}
Pretty much the same syntax using Java8 Streams
ArrayList<String> numbers = new ArrayList<String>();
numbers.add("one");
numbers.add("two");
numbers.add("three");
numbers.stream().forEach(num ->
{
System.out.println(numbers.indexOf(num) + " " + num);
});