Related
I need to group a given sort list by some given "blocks" or "groups" of elements. For example:
Given a list:
[A, B, C, D, E, F, G, H, I, J]
And groups
[A, C, D]
[F, E]
[J, H, I]
the result should be
[A, C, D, B, F, E, G, J, H, I]
The blocks of elements can not be mixed with non-group elements. The blocks should have the same order. The other elements of the list should mantain their order.
I have already found a solution. But it's not the most efficient code as you will see.
I'm using java 6 also...
public static List<CategoryProduct> sortProductsByBlocks(List<CategoryProduct> products, CategoryBlocks categoryBlocks) {
if (!validateCategoryBlocks(categoryBlocks)) {
return products;
}
Map<String, BlockView> mapProductByBlock = mapBlocksByPartnumber(categoryBlocks);
Map<String, BlockView> mapFirstProductByBlock = mapFirstProductByBlock(categoryBlocks);
Map<Integer, Block> blocksById = blocksById(categoryBlocks);
List<CategoryProduct> sortedProduct = Lists.newArrayList();
Map<String, CategoryProduct> productsMapByPartNumber = ProductHelper.getProductsMapByPartNumber(products);
List<CategoryProduct> processedProducts = Lists.newArrayList();
int j = 0;
for (int i = 0; i < products.size(); i++) {
CategoryProduct product = products.get(i);
if (blocksById.isEmpty() && !processedProducts.contains(product)) {
sortedProduct.add(j++, product);
processedProducts.add(product);
}
if (!processedProducts.contains(product) && (mapFirstProductByBlock.get(product.getPartNumber()) != null
|| mapProductByBlock.get(product.getPartNumber()) == null)) {
BlockView blockView = mapProductByBlock.get(product.getPartNumber());
if (blockView != null) {
Block block = blocksById.get(blockView.getBlockId());
if (block == null) {
sortedProduct.add(j++, product);
continue;
}
for (BlockProduct blockProduct : block.getProducts()) {
CategoryProduct categoryProduct = productsMapByPartNumber.get(blockProduct.getPartnumber());
sortedProduct.add(j++, categoryProduct);
processedProducts.add(categoryProduct);
}
blocksById.remove(blockView.getBlockId());
} else {
sortedProduct.add(j++, product);
processedProducts.add(product);
}
}
}
return sortedProduct;
}
Any advice to improve and make it faster will be welcome.
(edit with the improved code)
public static List<CategoryProduct> sortProductsByBlocks2(List<CategoryProduct> products,
CategoryBlocks categoryBlocks) {
if (!validateCategoryBlocks(categoryBlocks)) {
return products;
}
Map<String, Integer> blocksIdByFirstPartnumber = Maps.newHashMap();
List<String> partnumbersInBlocks = Lists.newArrayList();
for (int k = 0; k < categoryBlocks.getBlocks().size(); k++) {
Block block = categoryBlocks.getBlocks().get(k);
if (block != null && block.getProducts() != null) {
for (int i = 0; i < block.getProducts().size(); i++) {
BlockProduct blockProduct = block.getProducts().get(i);
if (i == 0) {
blocksIdByFirstPartnumber.put(blockProduct.getPartnumber(), k);
} else {
partnumbersInBlocks.add(blockProduct.getPartnumber());
}
}
}
}
CategoryProduct[] result = new CategoryProduct[products.size()];
Map<String, Integer> productsIndex = Maps.newHashMap();
Map<String, CategoryProduct> categoryProductByPartnumber = Maps.newHashMap();
int indexResult = 0;
for (CategoryProduct categoryProduct : products) {
String partNumber = categoryProduct.getPartNumber();
if (!partnumbersInBlocks.contains(partNumber)) {
if (blocksIdByFirstPartnumber.get(partNumber) != null) {
Block categoryProductBlock = categoryBlocks.getBlocks()
.get(blocksIdByFirstPartnumber.get(partNumber));
result[indexResult] = categoryProduct;
indexResult++;
for (int i = 1; i < categoryProductBlock.getProducts().size(); i++) {
BlockProduct blockProduct = categoryProductBlock.getProducts().get(i);
if (categoryProductByPartnumber.get(blockProduct.getPartnumber()) != null) {
result[indexResult] = categoryProductByPartnumber.get(blockProduct.getPartnumber());
} else {
productsIndex.put(blockProduct.getPartnumber(), indexResult);
result[indexResult] = null;
}
indexResult++;
}
} else {
result[indexResult] = categoryProduct;
indexResult++;
}
} else {
if (productsIndex.get(partNumber) != null) {
result[productsIndex.get(partNumber)] = categoryProduct;
} else {
categoryProductByPartnumber.put(partNumber, categoryProduct);
}
}
}
return Lists.newArrayList(Arrays.asList(result));
}
Performance:
Elements New algorithm Old algorithm
1200 0.002s 0.129s
12000 0.021s 14.673s
Form the code you submitted, I cannot figure out how your algorithm is fully working.
I can write another algorithm that will do the task.
Mark the first element for each group
[A,C,D] -> A
Remove from list(to_be_sorted) all elements from groups that are not marked
[A,C,D] -> remove [C,D]
perform sort on list
result ([A,B,F,G,J])
place removed element based on Mark
Initial Sorted List [A,B,F,G,J]
A->add [C,D]
List is [A,C,D,B,F,G,J]
B->as it is
F->add [E]
List is [A,C,D,B,F,E,G,J]
G->as it is
J->add [H,I]
Final Sorted List [A,C,D,B,F,E,G,J,H,I]
Time complexity is the same as sorting algorithm
By your definition it isn't entirely clear what the conditions are to merge the results from your given list and 'groups' ( arrays ). However, here is a solution based on your requirements using the assertion
"You want the first element of the list not contained in any of the groups inserted between the groups... "
public class MergeArrays {
private static final List<String> FIRST = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I", "J"));
private static final List<String> SECOND = new ArrayList<>(Arrays.asList("A", "C", "D"));
private static final List<String> THIRD = new ArrayList<>(Arrays.asList("F", "E"));
private static final List<String> FOURTH = new ArrayList<>(Arrays.asList("J", "H", "I"));
public static List<String> merge(List<String> source, List<String>... lists) {
List<String> result = new ArrayList<>();
for (List<String> list : lists) {
for (String value : list) {
source.remove(value);
}
}
for (List<String> list : lists) {
String value = null;
if (source.size() > 0) {
value = source.get(0);
source.remove(0);
}
result.addAll(merge(value, list));
}
return result;
}
public static List<String> merge(String value, List<String> list) {
List<String> result = new ArrayList<>(list);
if (value != null) {
result.add(value);
}
return result;
}
public static void main(String[] args) {
List<String> result = merge(FIRST, SECOND, THIRD, FOURTH);
System.out.println(result);
}
}
//Results
[A, C, D, B, F, E, G, J, H, I]
Starting with a set of sets "groups":
Set<Set<String>> groups = new HashSet<>();
I want to create a new list of sets by merging all subsets with common elements:
i.e. Starting with the sets below:
A = {a, b, c}
B = {c, d, e, f}
C = {f, g, h, i, j}
D = {k, l, m}
E = {m, n, o}
F = {p, q, r}
The final result would be:
Set 1 = {a, b, c, d, e, f, g, h, i, j}
Set 2 = {k, l, m, n, o}
Set 3 = {p, q, r}
Any advice on how to accomplish this would be appreciated.
EDIT: In case of uneven sets it would perform the same. So if it were a method, it pseudo would look like this:
public void doStuff(){
Set<Set<String>> groups = {{a,b,c}, {c,d,e,f}, {m, n, o}}
Set<Set<String>> newGroups = mergeSubsets(groups);
System.out.println(newGroups);
}
public Set<Set<String>> mergeSubsets(Set<Set<String>> groups){
//some operations
}
Console out:
New Groups: {{a,b,c,d,e,f}, {m, n, o}}
You can just implement the algorithm as you describe it in your problem statement -- find intersecting sets and merge them until there is nothing to merge. Standard library has a method Collections.disjoint that helps by determining if two collections have any elements in common:
// this implementation sacrifices efficiency for clarity
public Set<Set<String>> mergeSubsets(Set<Set<String>> groups) {
Set<Set<String>> result = new HashSet<>();
for (Set<String> set : groups) {
// try to find a set in result that intersects this set
// if one is found, merge the two. otherwise, add this set to result
result.stream()
.filter(x -> !Collections.disjoint(x, set))
.findAny()
.ifPresentOrElse( // this method was added in java 9
x -> x.addAll(set),
() -> result.add(new HashSet<>(set))
);
}
// if nothing got merged we are done; otherwise, recurse and try again
return result.size() == groups.size() ? result : mergeSubsets(result);
}
Here is the imperative way based on #NiksVij solution. Obviously the solution of #NiksVij is not correct and this answer aims to fix this and extend a bit more:
public class MergeSet {
public static void main(String... args) {
List<Set<String>> list = new ArrayList<>();
String[] A = {"a", "c", "e", "g"};
String[] B = {"b", "d", "f", "h"};
String[] C = {"c", "e", "f"};
String[] D = {"b"};
list.add(new HashSet<>(Arrays.asList(A)));
list.add(new HashSet<>(Arrays.asList(C)));
list.add(new HashSet<>(Arrays.asList(B)));
list.add(new HashSet<>(Arrays.asList(D)));
List<Set<String>> newGroups = merge(list);
System.out.println(newGroups);
}
#SuppressWarnings("empty-statement")
private static <T> List<Set<T>> merge(List<Set<T>> list) {
if (list == null || list.isEmpty()) {
return list;
}
List<Set<T>> merged = new ArrayList<>();
do {
merged.add(list.get(0));
list.remove(0);
while (mergeStep(merged.get(merged.size() - 1), list));
} while (!list.isEmpty());
return merged;
}
private static <T> boolean mergeStep(Set<T> setToCheck, List<Set<T>> remainingList) {
boolean atLeastOnceMerged = false;
Iterator<Set<T>> iterator = remainingList.iterator();
while (iterator.hasNext()) {
Set<T> elements = iterator.next();
boolean doMerge = !Collections.disjoint(elements, setToCheck);
if (doMerge) {
atLeastOnceMerged |= doMerge;
setToCheck.addAll(elements);
iterator.remove();
}
}
return atLeastOnceMerged;
}
import java.util.*;
public class MergeSet {
public static void main(String... args) {
List<Set<String>> groups = new ArrayList<>();
String[] A = {"a", "b", "c"};
String[] B = {"c", "d", "e", "f"};
String[] C = {"f", "g", "h", "i", "j"};
String[] D = {"k", "l", "m"};
String[] E = {"m", "n", "o"};
String[] F = {"p", "q", "r"};
groups.add(new HashSet<>(Arrays.asList(A)));
groups.add(new HashSet<>(Arrays.asList(B)));
groups.add(new HashSet<>(Arrays.asList(C)));
groups.add(new HashSet<>(Arrays.asList(D)));
groups.add(new HashSet<>(Arrays.asList(E)));
groups.add(new HashSet<>(Arrays.asList(F)));
Set<Set<String>> newGroups = mergeSubsets(groups);
System.out.println(newGroups);
}
private static Set<Set<String>> mergeSubsets(List<Set<String>> groups) {
List<Set<String>> newGroups = new ArrayList<>();
Set<String> init = groups.get(0);
groups.remove(0);
newGroups.add(init);
while (!groups.isEmpty()) {
removeMergedElementFromGroupAndUpdateNewGroup(newGroups.get(newGroups.size() - 1), groups);
if(!groups.isEmpty()) {
init = groups.get(0);
groups.remove(0);
newGroups.add(init);
}
}
return new HashSet<>(newGroups);
}
private static void removeMergedElementFromGroupAndUpdateNewGroup(Set<String> master2, List<Set<String>> masterList) {
Iterator<Set<String>> iterator = masterList.iterator();
while (iterator.hasNext()) {
Set<String> strings = iterator.next();
boolean merge = strings.stream().anyMatch(string -> master2.contains(string));
if (merge) {
master2.addAll(strings);
iterator.remove();
}
}
}
}
Hope this helps instead of Set<Set<String>> groups I have used List<Set<String>> groups for the ease of using lists if you have a constraint of using Set only , you can generate List from Set(say yourSet) by passing it into the constructor of Lists implementation , for eg.
groups = new ArrayList<>(yourSet);
I have two arraylists and I want to remove identical values between these lists, but only one occurrence of this. For example:
ArrayList<Integer> a = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));
ArrayList<Integer> b = new ArrayList<>(Arrays.asList(1,2,3,1,2,3));
With these, function unique(a,b) would return:
[[4,5,6],[1,2,3]]
Assuming you want to return an ArrayList of ArrayLists, you can achieve this using this method:
private static ArrayList<ArrayList<Integer>> unique(ArrayList<Integer> a, ArrayList<Integer> b) {
ArrayList<ArrayList<Integer>> unique = new ArrayList<>();
unique.add(new ArrayList<>());
unique.add(new ArrayList<>());
for (Integer i: a) {
if (!b.contains(i) && !unique.get(0).contains(i)) {
unique.get(0).add(i);
}
}
for (Integer i: b) {
if (a.contains(i) && !unique.get(1).contains(i)) {
unique.get(1).add(i);
}
}
return unique;
}
If you only need to know the unique elements in a bunch of lists:
public static <T> ArrayList getUnique(List<T> ... lists){
Set<T> unique = new HashSet<T>();
for (List<T> eachList : lists){
unique.addAll(eachList);
}
return new ArrayList(unique);
}
And you can call it like this:
List<Integer> unique = getUnique(a,b);
#lmiguelvargasf - I took your function and modified it a little because it turns out your version only works if the numbers are all different already. Here is the new function:
private static ArrayList<ArrayList<Integer>> unique(ArrayList<Integer> a, ArrayList<Integer> b) {
ArrayList<ArrayList<Integer>> unique = new ArrayList<>();
unique.add(new ArrayList<>());
unique.add(new ArrayList<>());
for (Integer i: a) {
if (!b.contains(i)) {
unique.get(0).add(i);
}else{
b.remove(i);
}
}
for (Integer i: b) {
if (!a.contains(i)) {
unique.get(1).add(i);
}else{
b.remove(i);
}
}
return unique;
}
I have a number of ArrayList with each ArrayList having objects and each one can have different length. I need to generate permutation like in the below example:
Suppose I have 2 ArrayList:
ArrayList A has object a, object b and object c
ArrayList B has object d, object e
Then the output should be 6 new ArrayList with these combinations:
Combination 1 object a and object d,
Combination 2 object a and object e,
Combination 3 object b and object d,
Combination 4 object b and object e,
Combination 5 object c and object d,
Combination 6 object c and object e,
Can anyone help me?
Guava 19+
Lists.cartesianProduct(List...)
E.g.:
List<Object> list1 = Arrays.asList("a", "b", "c");
List<Object> list2 = Arrays.asList("d", "e");
System.out.println(Lists.cartesianProduct(list1, list2));
Output:
[[a, d], [a, e], [b, d], [b, e], [c, d], [c, e]]
With Java8 streams
List<String> a = Arrays.asList("a", "b", "c");
List<String> b = Arrays.asList("d", "e");
String[][] AB = a.stream()
.flatMap(ai -> b.stream()
.map(bi -> new String[]{ai, bi}))
.toArray(String[][]::new);
System.out.println(Arrays.deepToString(AB));
output
[[a, d], [a, e], [b, d], [b, e], [c, d], [c, e]]
To get as List
List<List<String>> ll = a.stream()
.flatMap(ai -> b.stream()
.map(bi -> new ArrayList<>(Arrays.asList(ai, bi))))
.collect(Collectors.toList());
With an Iterable+Iterator:
import java.util.*;
class CartesianIterator <T> implements Iterator <List <T>> {
private final List <List <T>> lilio;
private int current = 0;
private final long last;
public CartesianIterator (final List <List <T>> llo) {
lilio = llo;
long product = 1L;
for (List <T> lio: lilio)
product *= lio.size ();
last = product;
}
public boolean hasNext () {
return current != last;
}
public List <T> next () {
++current;
return get (current - 1, lilio);
}
public void remove () {
++current;
}
private List<T> get (final int n, final List <List <T>> lili) {
switch (lili.size ())
{
case 0: return new ArrayList <T> (); // no break past return;
default: {
List <T> inner = lili.get (0);
List <T> lo = new ArrayList <T> ();
lo.add (inner.get (n % inner.size ()));
lo.addAll (get (n / inner.size (), lili.subList (1, lili.size ())));
return lo;
}
}
}
}
class CartesianIterable <T> implements Iterable <List <T>> {
private List <List <T>> lilio;
public CartesianIterable (List <List <T>> llo) {
lilio = llo;
}
public Iterator <List <T>> iterator () {
return new CartesianIterator <T> (lilio);
}
}
You can use them in a simplified for-loop:
class CartesianIteratorTest {
public static void main (String[] args) {
List <Character> la = Arrays.asList (new Character [] {'a', 'b', 'c'});
List <Character> lb = Arrays.asList (new Character [] {'d', 'e'});
List <List <Character>> llc = new ArrayList <List <Character>> ();
llc.add (la);
llc.add (lb);
CartesianIterable <Character> ci = new CartesianIterable <Character> (llc);
for (List<Character> lo: ci)
show (lo);
}
public static void show (List <Character> lo) {
System.out.print ("(");
for (Object o: lo)
System.out.print (o);
System.out.println (")");
}
}
Cartesian product of multiple lists using the map and reduce approach
The map method represents each element of the list as a singleton list and specifies the format of the result.
Intermediate output:
[[a], [b], [c]]
[[d], [e]]
[[f]]
The reduce method sums pairs of 2D lists into a single 2D list.
Final output:
[[a, d, f], [a, e, f], [b, d, f], [b, e, f], [c, d, f], [c, e, f]]
Try it online!
public static void main(String[] args) {
List<String> a = Arrays.asList("a", "b", "c");
List<String> b = Arrays.asList("d", "e");
List<String> c = Arrays.asList("f");
List<List<String>> cp = cartesianProduct(Arrays.asList(a, b, c));
// output
System.out.println(cp);
}
public static <T> List<List<T>> cartesianProduct(List<List<T>> lists) {
// check if not null
if (lists == null) return null;
// cartesian product of multiple lists
return lists.stream()
// only those lists that are not null and not empty
.filter(list -> list != null && list.size() > 0)
// represent each list element as a singleton list
.map(list -> list.stream().map(Collections::singletonList)
// Stream<List<List<T>>>
.collect(Collectors.toList()))
// intermediate output
.peek(System.out::println)
// stream of lists into a single list
.reduce((lst1, lst2) -> lst1.stream()
// combinations of inner lists
.flatMap(inner1 -> lst2.stream()
// concatenate into a single list
.map(inner2 -> Stream.of(inner1, inner2)
.flatMap(List::stream)
.collect(Collectors.toList())))
// list of combinations
.collect(Collectors.toList()))
// otherwise an empty list
.orElse(Collections.emptyList());
}
See also: Cartesian product of an arbitrary number of sets
Use Guava... Here is an example of a Cartesian product of a list with itself:
public static void main(String[] args) {
//How to do a cartesian product of a List of items
List<Integer> listToSelfMultiply = Arrays.asList(
new Integer(1), new Integer(2), new Integer(3), new Integer(4));
LinkedList<Integer> linkedListCopy = Lists.newLinkedList(listToSelfMultiply);
for (Integer i : listToSelfMultiply) {
if (linkedListCopy.size() == 1) {
break;
}
linkedListCopy.remove();
System.out.println("" + Arrays.deepToString(
Lists.cartesianProduct(Arrays.asList(i), linkedListCopy).toArray()) + "");
}
}
Use nested for loops that would have a loop for every ArrayList as below. I am assuming I have two ArrayLists - intList and stringList. I can have two nested for loops (one for each list) to generate the permutation.
for (Integer i : intList) {
for (String s : stringList) {
...
}
}
Cartesian product of multiple lists
You can use the reduce method with three parameters:
identity - specify the result stub.
List<List<T>>
accumulator - append elements of lists to the result.
List<List<T>> result, List<T> list
combiner - is used in parallel mode, combines the results.
List<List<T>> result1, List<List<T>> result2
Try it online!
/**
* #param lists the lists for multiplication
* #param <T> the type of list element
* #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();
return lists.stream()
// non-null and non-empty lists
.filter(list -> list != null && list.size() > 0)
// stream of lists into a single list
.reduce(// identity - specify the result stub
Collections.singletonList(Collections.emptyList()),
// accumulator - append elements of lists to the result
(result, list) -> result.stream()
.flatMap(inner -> list.stream()
.map(el -> {
List<T> nList = new ArrayList<>(inner);
nList.add(el);
return nList;
}))
// list of combinations
.collect(Collectors.toList()),
// combiner - is used in parallel mode, combines the results
(result1, result2) -> {
result1.addAll(result2);
return result1;
});
}
public static void main(String[] args) {
List<String> l1 = Arrays.asList("A", "B");
List<String> l2 = Arrays.asList("C", "D");
List<String> l3 = Arrays.asList("E", "F");
List<List<String>> cp = cartesianProduct(Arrays.asList(l1, l2, l3));
// output
System.out.println(cp);
}
Output:
[[A,C,E],[A,C,F],[A,D,E],[A,D,F],[B,C,E],[B,C,F],[B,D,E],[B,D,F]]
See also: Cartesian product of 3 collections
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);
});