Related
I was looking for something implementing the Collection interface where I can add multiple instances of the same object (based on the given Comparator), but the collection cannot contain twice the same object identity (based on the == operator). The collection has to be sorted and I must be able to remove one particular element (based on the == operator).
In other words, it has to satisfy the following testcase :
public MyCollection<E> implements Collection<E>
{ ... }
public class MyCollectionTest extends TestCase
{
static class MyComparator implements Comparator<MyInterface>
{
#Override
public int compare(MyInterface pO1, MyInterface pO2)
{
// the return type of getCategory() is just an enum.
return pO1.getCategory().ordinal() - pO2.getCategory().ordinal();
}
}
public void testAdd()
{
MyCollection<MyInterface> sut = new MyCollection<MyInterface>(new MyComparator());
MyInterface svc1 = EasyMock.createNiceMock(MyInterface.class);
MyInterface svc2 = EasyMock.createNiceMock(MyInterface.class);
EasyMock.expect(svc1.getCategory()).andStubReturn(Category.CAR);
EasyMock.expect(svc2.getCategory()).andStubReturn(Category.VAN);
EasyMock.replay(svc1, svc2);
sut.add(svc1);
sut.add(svc2);
assertEquals(2, sut.size());
assertSame(svc1, sut.last());
assertSame(svc2, sut.first());
}
public void testAddDouble()
{
MyCollection<MyInterface> sut = new MyCollection<MyInterface>(new MyComparator());
MyInterface svc1 = EasyMock.createNiceMock(MyInterface.class);
EasyMock.expect(svc1.getCategory()).andStubReturn(Category.CAR);
sut.add(svc1);
sut.add(svc1);
assertEquals(1, sut.size());
}
public void testRemove()
{
MyCollection<MyInterface> sut = new MyCollection<MyInterface>(new MyComparator());
MyInterface svc1 = EasyMock.createNiceMock(MyInterface.class);
MyInterface svc2 = EasyMock.createNiceMock(MyInterface.class);
EasyMock.expect(svc1.getCategory()).andStubReturn(Category.VAN);
EasyMock.expect(svc2.getCategory()).andStubReturn(Category.VAN);
EasyMock.replay(svc1, svc2);
sut.add(svc1);
sut.add(svc2);
assertEquals(2, sut.size());
sut.remove(svc1);
assertEquals(1, sut.size());
}
}
Any help ?
Thank you!
EDIT: Actually I think this can be solved with just new TreeSet<>(Ordering.natural().thenComparing(Ordering.arbitrary())) (with Guava's Ordering)
If you don't have Guava, you could roll your own using TreeMap and IdentityHashMap, something like:
public class IdentityTreeSet<T extends Comparable> extends AbstractCollection<T> {
private SortedMap<T, Set<T>> values = new TreeMap<>();
#Override
public Iterator<T> iterator() {
return new Iterator<T>() {
Iterator<Set<T>> outerIterator = values.values().iterator();
Set<T> currentSet = Collections.newSetFromMap(new IdentityHashMap<>());
Iterator<T> innerIterator = currentSet.iterator();
#Override
public boolean hasNext() {
return innerIterator.hasNext() || outerIterator.hasNext();
}
#Override
public T next() {
if (innerIterator.hasNext()) {
return innerIterator.next();
} else {
currentSet = outerIterator.next();
innerIterator = currentSet.iterator();
return next();
}
}
#Override
public void remove() {
innerIterator.remove();
if (currentSet.isEmpty()) {
outerIterator.remove();
}
}
};
}
#Override
public int size() {
int i = 0;
for (Set<T> set : values.values()) {
i += set.size();
}
return i;
}
#Override
public boolean add(T e) {
Set<T> entrySet = values.get(e);
if (entrySet == null) {
Set<T> newSet = Collections.newSetFromMap(new IdentityHashMap<>());
newSet.add(e);
values.put(e, newSet);
return true;
} else {
return entrySet.add(e);
}
}
#Override
public boolean remove(Object o) {
Set<T> entrySet = values.get(o);
if (entrySet == null) {
return false;
} else {
boolean removed = entrySet.remove(o);
if (entrySet.isEmpty()) {
values.remove(o);
}
return removed;
}
}
}
Note that the documentation for Collection.remove is written in terms of equals, so this class cannot strictly adhere to the Collection contract, and may cause errors if passed as a Collection to code you don't control.
If no existing collection does exactly what you want, consider the following strategy:
Define a class whose public methods do exactly what you need, no more and no less.
Implement the class using existing collections to take care of the busy work, but with your code in control to impose your requirements.
For example, your class might have a TreeSet each of whose elements is a non-empty IdentityHashMap of the underlying class. The TreeSet comparator would pull an element from each IdentityHashMap and return the result of comparing them.
To remove an element, first check whether removing it would empty its IdentityHashMap (it is present, and the set size is 1). If so, remove the IdentityHashMap from the TreeSet. If not, remove the element from its IdentityHashMap.
This is only an outline, with a lot of details that need to be filled in. The main point is the idea of building exactly what you want, based on what already exists wrapped in a class you write.
Concerning this part of your question "but the collection cannot contain twice the same object identity (based on the == operator)", if you want two objects to be both equal by equals and == operator, you need to read about instance controlled objects (basically objects that hash their own instances, and return you the same cached object instead of creating a new one when the object being requested already exists).
I am using this code to convert a Set to a List:
Map<String, List<String>> mainMap = new HashMap<>();
for (int i=0; i < something.size(); i++) {
Set<String> set = getSet(...); //returns different result each time
List<String> listOfNames = new ArrayList<>(set);
mainMap.put(differentKeyName, listOfNames);
}
I want to avoid creating a new list in each iteration of the loop. Is that possible?
You can use the List.addAll() method. It accepts a Collection as an argument, and your set is a Collection.
List<String> mainList = new ArrayList<String>();
mainList.addAll(set);
EDIT: as respond to the edit of the question.
It is easy to see that if you want to have a Map with Lists as values, in order to have k different values, you need to create k different lists.
Thus: You cannot avoid creating these lists at all, the lists will have to be created.
Possible work around:
Declare your Map as a Map<String,Set> or Map<String,Collection> instead, and just insert your set.
Use constructor to convert it:
List<?> list = new ArrayList<>(set);
Also from Guava Collect library, you can use newArrayList(Collection):
Lists.newArrayList([your_set])
This would be very similar to the previous answer from amit, except that you do not need to declare (or instanciate) any list object.
We can use following one liner in Java 8:
List<String> list = set.stream().collect(Collectors.toList());
Here is one small example:
public static void main(String[] args) {
Set<String> set = new TreeSet<>();
set.add("A");
set.add("B");
set.add("C");
List<String> list = set.stream().collect(Collectors.toList());
}
the simplest solution
I wanted a very quick way to convert my set to List and return it, so in one line I did
return new ArrayList<Long>(mySetVariable);
Since it hasn't been mentioned so far, as of Java 10 you can use the new copyOf factory method:
List.copyOf(set);
From the Javadoc:
Returns an unmodifiable List containing the elements of the given Collection, in its iteration order.
Note that this creates a new list (ImmutableCollections$ListN to be precise) under the hood by
calling Collection#toArray() on the given set and then
putting these objects into a new array.
I would do :
Map<String, Collection> mainMap = new HashMap<String, Collection>();
for(int i=0; i<something.size(); i++){
Set set = getSet(...); //return different result each time
mainMap.put(differentKeyName,set);
}
You could use this one line change: Arrays.asList(set.toArray(new Object[set.size()]))
Map<String, List> mainMap = new HashMap<String, List>();
for(int i=0; i<something.size(); i++){
Set set = getSet(...);
mainMap.put(differentKeyName, Arrays.asList(set.toArray(new Object[set.size()])));
}
Java 8 provides the option of using streams and you can get a list from Set<String> setString as:
List<String> stringList = setString.stream().collect(Collectors.toList());
Though the internal implementation as of now provides an instance of ArrayList:
public static <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
but JDK does not guarantee it. As mentioned here:
There are no guarantees on the type, mutability, serializability, or
thread-safety of the List returned; if more control over the returned
List is required, use toCollection(Supplier).
In case you want to be sure always then you can request for an instance specifically as:
List<String> stringArrayList = setString.stream()
.collect(Collectors.toCollection(ArrayList::new));
For the sake of completeness...
Say that you really do want to treat the Map values as Lists, but you want to avoid copying the Set into a List each time.
For instance, maybe you are calling one library function that creates a Set, but you are passing your Map<String, List<String>> result to a (poorly-designed but out of your hands) library function that only takes Map<String, List<String>>, even though somehow you know that the operations it does with the Lists are equally applicable to any Collection (and thus any Set). And for some reason you need to avoid the speed/memory overhead of copying each Set to a List.
In this super niche case, depending on the (maybe unknowable) behavior the library function needs out of your Lists, you may be able to create a List view over each Set. Note that this is inherently unsafe (because the library function's requirements from each List could presumably change without you knowing), so another solution should be preferred. But here's how you'd do it.
You'd create a class that implements the List interface, takes a Set in the constructor and assigns that Set to a field, and then uses that internal Set to implement the List API (to the extent possible, and desired).
Note that some List behavior you simply will not be able to imitate without storing the elements as a List, and some behavior you will only partially be able to imitate. Again, this class is not a safe drop-in replacement for Lists in general. In particular, if you know that the use case requires index-related operations or MUTATING the List, this approach would go south very fast.
public class ListViewOfSet<U> implements List<U> {
private final Set<U> wrappedSet;
public ListViewOfSet(Set<U> setToWrap) { this.wrappedSet = setToWrap; }
#Override public int size() { return this.wrappedSet.size(); }
#Override public boolean isEmpty() { return this.wrappedSet.isEmpty(); }
#Override public boolean contains(Object o) { return this.wrappedSet.contains(o); }
#Override public java.util.Iterator<U> iterator() { return this.wrappedSet.iterator(); }
#Override public Object[] toArray() { return this.wrappedSet.toArray(); }
#Override public <T> T[] toArray(T[] ts) { return this.wrappedSet.toArray(ts); }
#Override public boolean add(U e) { return this.wrappedSet.add(e); }
#Override public boolean remove(Object o) { return this.wrappedSet.remove(o); }
#Override public boolean containsAll(Collection<?> clctn) { return this.wrappedSet.containsAll(clctn); }
#Override public boolean addAll(Collection<? extends U> clctn) { return this.wrappedSet.addAll(clctn); }
#Override public boolean addAll(int i, Collection<? extends U> clctn) { throw new UnsupportedOperationException(); }
#Override public boolean removeAll(Collection<?> clctn) { return this.wrappedSet.removeAll(clctn); }
#Override public boolean retainAll(Collection<?> clctn) { return this.wrappedSet.retainAll(clctn); }
#Override public void clear() { this.wrappedSet.clear(); }
#Override public U get(int i) { throw new UnsupportedOperationException(); }
#Override public U set(int i, U e) { throw new UnsupportedOperationException(); }
#Override public void add(int i, U e) { throw new UnsupportedOperationException(); }
#Override public U remove(int i) { throw new UnsupportedOperationException(); }
#Override public int indexOf(Object o) { throw new UnsupportedOperationException(); }
#Override public int lastIndexOf(Object o) { throw new UnsupportedOperationException(); }
#Override public ListIterator<U> listIterator() { throw new UnsupportedOperationException(); }
#Override public ListIterator<U> listIterator(int i) { throw new UnsupportedOperationException(); }
#Override public List<U> subList(int i, int i1) { throw new UnsupportedOperationException(); }
}
...
Set<String> set = getSet(...);
ListViewOfSet<String> listOfNames = new ListViewOfSet<>(set);
...
I create simple static method:
public static <U> List<U> convertSetToList(Set<U> set)
{
return new ArrayList<U>(set);
}
... or if you want to set type of List you can use:
public static <U, L extends List<U>> List<U> convertSetToList(Set<U> set, Class<L> clazz) throws InstantiationException, IllegalAccessException
{
L list = clazz.newInstance();
list.addAll(set);
return list;
}
Recently I found this:
ArrayList<T> yourList = Collections.list(Collections.enumeration(yourSet<T>));
I found this working fine and useful to create a List from a Set.
ArrayList < String > L1 = new ArrayList < String > ();
L1.addAll(ActualMap.keySet());
for (String x: L1) {
System.out.println(x.toString());
}
You convert Set to List without adding ordering information (like sorting) just to store it in the map.
Because Set is unordered and no ordering information is added, List should not be used, as it will contain randomly ordered data and all it's methods that are related to ordered data will be ambiguous.
You should use Collection interface instead, that accepts both Set and List in the map. This way, no additional memory is required as you use polymorphism instead of copying data.
Map<String, Collection<String>> mainMap = new HashMap<>();
for (int i=0; i < something.size(); i++) {
Set<String> set = getSet(...); //returns different result each time
mainMap.put(differentKeyName, set);
}
Disclaimer: my edit to a similar answer was rejected so I added my own answer with additional information
Map<String, List> mainMap = new HashMap<String, List>();
for(int i=0; i<something.size(); i++){
Set set = getSet(...); //return different result each time
mainMap.put(differentKeyName, new ArrayList(set));
}
I'm obviously missing something here, as this sound basic enough but yet...
I have a collection of objects . I need to use each one of them as parameter in constructor for a new object and return each new object to the caller method, one by one.
But -if I loop over the collection obviously the loop only runs once, and only returns the 1st object.
Edit : Returning the whole collection or some new collection will not work because :
The caller method [not mine to change] runs inside a start() method of a Runnable ThingProvider, which returns a single Thing whenever a request is submitted to it. So, returning List is not possible.
Thanks :)
public List<T> loop(Collection<? extends U> coll) {
List<T> a = new ArrayList<T>();
for (U u : coll){
a.add(new T(u));
}
return a;
}
Return a custom Iterator. Assumming your new objects are of class MyObject and the constructor accepts an Object:
public Iterator<MyObject> myObjectsIterator(final Iterator<? extends Object> it) {
return new Iterator<MyObject>() {
public boolean hasNext() {
return it.hasNext();
}
public MyObject next() {
return new MyObject(it.next());
}
public void remove() {
it.remove();
}
};
}
And you would call it like this:
...
Iterator<MyObject> myIt = myObjectsIterator(myListOfObjects.iterator());
// Now you can pass myIt around as a normal object. It will remember
// which one is the next Object with which to construct a MyObject
// and will generate it on the fly
...
while (myIt.hasNext()) { // is there any MyObject remaining?
MyObject myObj = myIt.next(); // gets the next MyObject
// do something with myObj
}
...
This is a poorly worded question and I think as others have noted, just returning a new list of the objects is fine. But if you really want to process them one at a time while you're looping through it, you can use the command pattern.
public interface Command {
void execute(NewType object);
}
Now in your caller method, you can do the following:
public void doSomething() {
processList(myList, new Command() {
void execute(NewType object) {
// Do whatever you want with this object
}
});
}
And, in the method that will actually go through the list:
public void processList(Iterable<OldType> values, Command command) {
for(OldType v : values) {
NewType newType = new NewType(v);
command.execute(newType);
}
}
In java you can return only once. So if you want to get some informations from your methods either you wrap them into a "Big" Object (here a List) or you give to the method the means to put informations in your parameters.
You could have something like this :
public static void main(String... args){
List<Parameter> parameters = methodToGetParameters();
List<Result> results = generateObjectsFromList(parameters);
for(Result result : results){
handleAResult(result);
}
}
public List<Result> generateObjectsFromList(List<Parameter> parameters){
List<Result> results = new ArrayList<Result>();
for(Parameter parameter : parameters){
results.add(new Result(parameter));
}
return results;
}
Or like this :
public static void main(String... args){
List<Parameter> parameters = methodToGetParameters();
List<Result> results = new ArrayList<Result>();
generateObjectsFromList(parameters, results);
for(Result result : results){
handleAResult(result);
}
}
public void generateObjectsFromList(List<Parameter> parameters, List<Result> results){
for(Parameter parameter : parameters){
results.add(new Result(parameter));
}
}
A third way to do this would be to use fields, but it's not really good to have a lot of fields if they're not really used (or only by one method).
On the same topic :
Java Object Oriented Design Question: Returning multiple objects in java(Updated)
Using a java method to return multiple values?
Return a collection from the method and in the collection implement a custom iterator to transform the input collection to the new collection. The following code shows how to do it using the Google Guava library:
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
public class Test {
static class Person {
public final String name;
public Person(String name) {
this.name = name;
}
}
public static Collection<Person> peopleFromNames(Collection<String> names) {
return Collections2.transform(names, new Function<String, Person>() {
public Person apply(String name) {
return new Person(name);
}});
}
public static void main(String[] args) {
List<String> names = Arrays.asList("Brian", "Albert", "Roger");
for (Person person : peopleFromNames(names)) {
System.out.println(person.name);
}
}
}
do you mean using of delegates something like below
public class Test {
private static class Person{
private final String name;
Person(String name){
this.name = name;
}
#Override
public String toString() {
return return name;
}
}
private interface Printer {
void print(Object object);
}
public static void main(String[] args) {
final String[] names = {"one", "two", "three"};
final ArrayList<Person> people = construct(names, new Printer() {
#Override
public void print(Object object) {
System.out.println(object.toString());
}
});
}
private static ArrayList<Person> construct(String[] names, Printer printer) {
ArrayList<Person> people = new ArrayList<Person>();
for (String name : names) {
printer.print(new Person(name));
}
return people;
}
}
It's Possible.
Check these Project for Java-yield , yield4Java, infomancers
If you're using this just once in your entire code, You're better off choosing a method from the other answers.
Return a list of the new objects.
I'm writing an adapter framework where I need to convert a list of objects from one class to another. I can iterate through the source list to do this as in
Java: Best way of converting List<Integer> to List<String>
However, I'm wondering if there is a way to do this on the fly when the target list is being iterated, so I don't have to iterate through the list twice.
Java 8 way:
List<String> original = ...;
List<Wrapper> converted = original.stream().map(Wrapper::new).collect(Collectors.toList());
assuming Wrapper class has a constructor accepting a String.
My answer to that question applies to your case:
import com.google.common.collect.Lists;
import com.google.common.base.Functions
List<Integer> integers = Arrays.asList(1, 2, 3, 4);
List<String> strings = Lists.transform(integers, Functions.toStringFunction());
The transformed list is a view on the original collection, so the transformation happens when the destination List is accessed.
As an alternative to the iterator pattern, you can use a abstract generic mapper class, and only override the transform method:
create a generic collection mapper for any data type
[optional] create a library of methods that transform between different data types (and override the method)
use that library
the implementation:
// Generic class to transform collections
public abstract class CollectionTransformer<E, F> {
abstract F transform(E e);
public List<F> transform(List<E> list) {
List<F> newList = new ArrayList<F>();
for (E e : list) {
newList.add(transform(e));
}
return newList;
}
}
// Method that transform Integer to String
// this override the transform method to specify the transformation
public static List<String> mapIntegerToStringCollection(List<Integer> list) {
CollectionTransformer transformer = new CollectionTransformer<Integer, String>() {
#Override
String transform(Integer e) {
return e.toString();
}
};
return transformer.transform(list);
}
// Example Usage
List<Integer> integers = Arrays.asList(1,2);
List<String> strings = mapIntegerToStringCollection(integers);
This would be useful is you have to use transformations every time, encapsulating the process.
So you can make a library of collection mappers very easy.
If you are trying to get a list of elements within a list then use the below code.Here the list of objects contains attribute name and below gets you list of names from that list
inputList.stream().map(p -> p.getName()).collect(Collectors.toList());
You can write a mapping iterator that decorates an existing iterator and applies a function on it. In this case, the function transforms the objects from one type to another "on-the-fly".
Something like this:
import java.util.*;
abstract class Transformer<T, U> implements Iterable<U>, Iterator<U> {
public abstract U apply(T object);
final Iterator<T> source;
Transformer(Iterable<T> source) { this.source = source.iterator(); }
#Override public boolean hasNext() { return source.hasNext(); }
#Override public U next() { return apply(source.next()); }
#Override public void remove() { source.remove(); }
public Iterator<U> iterator() { return this; }
}
public class TransformingIterator {
public static void main(String args[]) {
List<String> list = Arrays.asList("1", "2", "3");
Iterable<Integer> it = new Transformer<String, Integer>(list) {
#Override public Integer apply(String s) {
return Integer.parseInt(s);
}
};
for (int i : it) {
System.out.println(i);
}
}
}
Lambdaj allows to do that in a very simple and readable way. For example, supposing you have a list of Integer and you want to convert them in the corresponding String representation you could write something like that;
List<Integer> ints = asList(1, 2, 3, 4);
Iterator<String> stringIterator = convertIterator(ints, new Converter<Integer, String> {
public String convert(Integer i) { return Integer.toString(i); }
});
Lambdaj applies the conversion function only while you're iterating on the result.
There is also a more concise way to use the same feature. The next example works supposing that you have a list of persons with a name property and you want to convert that list in an iterator of person's names.
Iterator<String> namesIterator = convertIterator(persons, on(Person.class).getName());
Pretty easy. Isn't it?
This Could be a solutions --> by using map
List<Employee> employee = Arrays.asList(new Emp(1, 100), new Emp(2, 200), new Emp(3, 300));
List<Employee> employeS = employee.stream()
.map(emp -> new Emp(emp.getId(), emp.getSalary * 100))
.collect(Collectors.toList());
employeS .stream() .forEach(s -> System.out.println("Id :" + s.getId() + " Salary :" + s.getSalary()));
That question does not iterate through the list twice. It just iterates once and by far is the only known method.
Also you could use some transformer classes in commons-collections of google-collections but they all do the same thing under the hood :) the following being one way
CollectionUtils.collect(collectionOfIntegers, new org.apache.commons.collections.functors.StringValueTransformer());
Well, you could create your own iterator wrapper class to do this. But I doubt that you would save much by doing this.
Here's a simple example that wraps any iterator to a String iterator, using Object.toString() to do the mapping.
public MyIterator implements Iterator<String> {
private Iterator<? extends Object> it;
public MyIterator(Iterator<? extends Object> it) {
this.it = it;
}
public boolean hasNext() {
return it.hasNext();
}
public String next() {
return it.next().toString();
}
public void remove() {
it.remove();
}
}
I think you would either have to create a custom List (implementing the List interface) or a custom Iterator. For example:
ArrayList<String> targetList = new ArrayList<String>();
ConvertingIterator<String> iterator = new ConvertingIterator<String>(targetList);
// and here you would have to use a custom List implementation as a source List
// using the Iterator created above
But I doubt that this approach would save you much.
Here's an on-the-fly approach. (There must be something already like this in the jdk; I just can't find it.)
package com.gnahraf.util;
import java.util.AbstractList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
/**
*
*/
public class Lists {
private Lists() { }
public static <U,V> List<V> transform(List<U> source, Function<U, V> mapper) {
return new ListView<U, V>(source, mapper);
}
protected static class ListView<U, V> extends AbstractList<V> {
private final List<U> source;
private final Function<U, V> mapper;
protected ListView(List<U> source, Function<U, V> mapper) {
this.source = Objects.requireNonNull(source, "source");
this.mapper = Objects.requireNonNull(mapper, "mapper");
}
#Override
public V get(int index) {
return mapper.apply(source.get(index));
}
#Override
public int size() {
return source.size();
}
}
}
Say I have 2 parallel collections, eg: a list of people's names in a List<String> and a list of their age in a List<Int> in the same order (so that any given index in each collection refers to the same person).
I want to iterate through both collections at the same time and fetch the name and age of each person and do something with it. With arrays this is easily done with:
for (int i = 0; i < names.length; i++) {
do something with names[i] ....
do something with ages[i].....
}
What would be the most elegant way (in terms of readability and speed) of doing this with collections?
it1 = coll1.iterator();
it2 = coll2.iterator();
while(it1.hasNext() && it2.hasNext()) {
value1 = it1.next();
value2 = it2.next();
do something with it1 and it2;
}
This version terminates when the shorter collection is exhausted; alternatively, you could continue until the longer one is exhausted, setting value1 resp. value2 to null.
I would create a new object that encapsulates the two. Throw that in the array and iterate over that.
List<Person>
Where
public class Person {
public string name;
public int age;
}
for (int i = 0; i < names.length; ++i) {
name = names.get(i);
age = ages.get(i);
// do your stuff
}
It doesn't really matter. Your code won't get points for elegance. Just do it so that it works. And please don't bloat.
You could create an interface for it:
public interface ZipIterator<T,U> {
boolean each(T t, U u);
}
public class ZipUtils {
public static <T,U> boolean zip(Collection<T> ct, Collection<U> cu, ZipIterator<T,U> each) {
Iterator<T> it = ct.iterator();
Iterator<U> iu = cu.iterator();
while (it.hasNext() && iu.hasNext()) {
if (!each.each(it.next(), iu.next()) {
return false;
}
}
return !it.hasNext() && !iu.hasNext();
}
}
And then you have:
Collection<String> c1 = ...
Collection<Long> c2 = ...
zip(c1, c2, new ZipIterator<String, Long>() {
public boolean each(String s, Long l) {
...
}
});
I took #cletus comment and Improved it abit, And that's what I use:
public static <T,U> void zip(Collection<T> ct, Collection<U> cu, BiConsumer<T, U> consumer) {
Iterator<T> it = ct.iterator();
Iterator<U> iu = cu.iterator();
while (it.hasNext() && iu.hasNext()) {
consumer.accept(it.next(), iu.next());
}
}
Usage:
zip(list1, list2, (v1, v2) -> {
// Do stuff
});
While the submitted solutions are correct I prefer the following one because it follows the guides from effective java item 57: minimize the scope of local variables:
for (Iterator<String> i = lst1.iterator(), ii = lst2.iterator(); i.hasNext() && ii.hasNext(); ) {
String e1 = i.next();
String e2 = ii.next();
....
}
As suggested by jeef3, modeling the true domain rather than keeping separate, implicitly coupled Lists is the right way to go... when this is an option.
There are various reasons why you might not be able to adopt this approach. If so...
A. You can use a callback approach, as suggested by cletus.
B. You can still choose to expose an Iterator that exposes domain object element for each composite instance. This approach doesn't force you to keep a parallel List structure around.
private List<String> _names = ...;
private List<Integer> _ages = ...;
Iterator<Person> allPeople() {
final Iterator<String> ni = _names.iterator();
final Iterator<Integer> ai = _ages.iterator();
return new Iterator() {
public boolean hasNext() {
return ni.hasNext();
}
public Person next() {
return new Person(ni.next(), ai.next());
}
public void remove() {
ni.remove();
ai.remove();
}
};
}
C. You can use a variation of this and use a RowSet style cursor API. Let's say IPerson is an interface that describes Person. Then we can do:
public interface IPerson {
String getName();
void setName(String name);
...
}
public interface ICursor<T> {
boolean next();
T current();
}
private static class PersonCursor implements IPerson, ICursor<IPerson> {
private final List<String> _names;
...
private int _index = -1;
PersonCursor(List<String> names, List<Integer> ages) {
_names = names;
...
}
public boolean next() {
return ++_index < _names.size();
}
public Person current() {
return this;
}
public String getName() {
return _names.get(_index);
}
public void setName(String name) {
_names.set(0, name);
}
...
}
private List<String> _names = ...;
private List<Integer> _ages = ...;
Cursor<Person> allPeople() {
return new PersonCursor(_names, _ages);
}
Note that the B approach also be made to support updates to list by introducing a Domain interface, and having the Iterator return 'live' objects.
I just posted this function in this similar question (which #Nils von Barth asserts is not a duplicate ;) ), but it's equally applicable here:
public static <L,R,M> List<M> zipLists(
BiFunction<L,R,M> factory, Iterable<L> left, Iterable<R> right) {
Iterator<L> lIter = left.iterator();
Iterator<R> rIter = right.iterator();
ImmutableList.Builder<M> builder = ImmutableList.builder();
while (lIter.hasNext() && rIter.hasNext()) {
builder.add(factory.apply(lIter.next(), rIter.next()));
}
// Most of the existing solutions fail to enforce that the lists are the same
// size. That is a *classic* source of bugs. Always enforce your invariants!
checkArgument(!lIter.hasNext(),
"Unexpected extra left elements: %s", ImmutableList.copyOf(lIter));
checkArgument(!rIter.hasNext(),
"Unexpected extra right elements: %s", ImmutableList.copyOf(rIter));
return builder.build();
}
You can then provide a factory operation for the BiFunction, such as a value-type's constructor:
List<Person> people = zipLists(Person::new, names, ages);
If you really just want to iterate over them and do some operation, rather than construct a new collection, you could swap the BiFunction for a BiConsumer and have the function return void.