Lets assume I have a class containing a List, e.g.
public static class ListHolder {
List<String> list = new ArrayList<>();
public ListHolder(final List<String> list) {
this.list = list;
}
public List<String> getList() {
return list;
}
}
Let's furthermore assume I have a whole list of instances of this class:
ListHolder listHolder1 = new ListHolder(Arrays.asList("String 1", "String 2"));
ListHolder listHolder2 = new ListHolder(Arrays.asList("String 3", "String 4"));
List<ListHolder> holders = Arrays.asList(listHolder1, listHolder2);
And now I need to extract all Strings to get a String List containing all Strings of all instances, e.g.:
[String 1, String 2, String 3, String 4]
With Guava this would look like this:
List<String> allString = FluentIterable
.from(holders)
.transformAndConcat(
new Function<ListHolder, List<String>>() {
#Override
public List<String> apply(final ListHolder listHolder) {
return listHolder.getList();
}
}
).toList();
My question is how can I achieve the same with the Java 8 stream API?
List<String> allString = holders.stream()
.flatMap(h -> h.getList().stream())
.collect(Collectors.toList());
Here is an older question about collection flattening: (Flattening a collection)
Related
I have a Map<String, List<MyObject> myMap. How can I join all the List values into one List<MyObject>, excluding the String key?
I tried with this
List<MyObject> list = new ArrayList<MyObject>(myMap.values())
but this does not work with a collection.
Also thought of just iterating over the Maps and joining each list to a new list, but was hoping for a better way.
here is a possible way with streams
map.values().stream().flatMap(Collection::stream).collect(Collectors.toList())
I have a sample, it may helps you:
public class App {
public static void main(String[] args) {
test();
}
private static void test() {
Map<String, List<Test>> map = new HashMap<>();
List<Test> test = new ArrayList<>();
test.add(new Test(1, "AAA"));
test.add(new Test(2, "BBB"));
map.put("A", test);
test = new ArrayList<>();
test.add(new Test(3, "CCC"));
test.add(new Test(4, "DDD"));
map.put("B", test);
System.out.println(map);
List<Test> testList = new ArrayList<>();
map.values().stream().forEach(tests -> tests.forEach(testData -> testList.add(testData)));
System.out.println(testList);
}
}
class Test {
private int id;
private String name;
//getter setters
}
I'm having trouble figuring out how to deal with cleaning up some of my code to do this:
I have a List of a Context objects. Each Context has a String userId and a Map<String, SomeObject> someObjects.
I want to flatten this into a Map<String, SomeObjects> where the kep is the userId. To be more concrete:
class Context {
String userId;
Map<String, List<SomeObject>> // the String here is something other than userId
// other stuff, getters/setters
}
Given List<Context>, I want to get Map<String, List<SomeObject> but where the String is actually the userId.
Is there a clean way to do this?
Assuming a Context has a String userid and a List<SomeObject> someObjects:
Map<String, Set<SomeObject>> map = contexts.stream()
.collect(Collectors.toMap(Context::getUserid,
c -> c.getSomeObjects().values().stream()
.flatMap(Collection::stream)
.collect(Collectors.toSet())
));
The key points here are the use of toMap() to collect by userid and flatmap() to turn a Stream<List<SomeObject>> into a Stream<SomeObject> so you can collect them into one collection.
Creating class Context to hold String and Map datatypes
class Context {
String userId;
Map<String, List<Integer>> map;
public Context(String s, List<Integer> list) {
userId = s;
map = new HashMap<>();
map.put(userId, list);
}
public void setValues(String s, List<Integer> list) {
map.put(s, list);
}
}
Now creating the Solution class which has List<Context>
public class Solution {
public static void main(String[] args) {
List<Integer> list;
// Context c1
list = new ArrayList<>(Arrays.asList(1, 2, 3));
Context c1 = new Context("dev", list);
list = new ArrayList<>(Arrays.asList(-1, -3));
c1.setValues("dev2", list);
list = new ArrayList<>(Arrays.asList(-6, -3));
c1.setValues("dev3", list);
// Context c2
list = new ArrayList<>(Arrays.asList(12, 15, 18));
Context c2 = new Context("macy", list);
list = new ArrayList<>(Arrays.asList(-12, -13));
c2.setValues("macy2", list);
list = new ArrayList<>(Arrays.asList(-8, -18));
c2.setValues("macy3", list);
// Context c3
list = new ArrayList<>(Arrays.asList(20, 30));
Context c3 = new Context("bob", list);
list = new ArrayList<>(Arrays.asList(-31, -32));
c3.setValues("bob2", list);
// Context List
List<Context> contextList = new ArrayList<>();
contextList.addAll(Arrays.asList(c1, c2, c3));
retrieveRecords(contextList);
}
private static void retrieveRecords(List<Context> contextList) {
// regular way of retrieving map values
for (Context c : contextList) {
System.out.println(c.map);
}
System.out.println();
// Retrieving only those records which has String key as userID
for (Context c : contextList) {
System.out.println(c.userId + "\t=>\t" + c.map.get(c.userId));
}
}
}
I trying to implement functionally similar to CollectionUtils transform (Apache Commons Collections)
class CollectionUtils {
public static void transformerModifier(Collection<MyClass> myCollection) {
// How should I implement this method in order that
// output from the line 1 and line 2 will be the same ?
}
public static List<String> transform(Collection<MyClass> myCollection) {
List<String> strCollection = new LinkedList<>();
for (MyClass item : myCollection) {
strCollection.add(item.getName());
}
return strCollection;
}
}
class myClass {
private String name;
private int value;
myClass( String name, int value) {
this.name = name ;
this.value = value;
}
public String toString(){
return new String(name+ ":" + value ) ;
}
}
class MyClassCollection{
private List<myClass> list ;
myClassCollection(List<myClass> list){
this.list = list;
}
List<myClass> collection(){
return list.clone();
}
}
public class TestClass{
public static void main (String[] args) {
List<MyClass> list = new ArrayList<>();
list.add(new myClass("John", 12);
list.add(new myClass("Mike", 16);
list.add(new myClass("Eric", 13);
list.add(new myClass("Mark", 142);
list.add(new myClass("Alex", 112);
MyClassCollection myOjb = new MyClassCollection(list );
CollectionUtils.transformerModifier(myObj.collection() );
List<MyClass> myList = CollectionUtils.transform(myObj.collection());
System.out.println(Arrays.toString(myObj.collection().toArray)); // line 1
System.out.println(Arrays.toString(myList.toArray)); // line 2
}
}
output: [John,Mike,Eric,Mark,Alex] // output after line 1
output: [John,Mike,Eric,Mark,Alex] // should be output after line 2
My question is it possible to implement method transformerModifier in the way that it will change collection of the object myObj so that myObj.collection() return not the List<myClass> but the List of List<String> ( where string is the data from private String name data member of myClass ) ?
My guess is that the solution should be through anonymous class. However, I didn't understand yet how should I implement it.
If you are using Java 8, you could make use of streams and map() to do something like this:
List<MyClass> myClassList = new ArrayList<>();
//add your items to myClassList here
List<String> names = myClassList.stream().map(MyClass::getName).collect(Collectors.toList());
//names will now consist of a List of all the names associated with
//each of the MyClass objects within myClassList in the same order
This solution makes use of Method Reference as well MyClass::getName. This calls the getName method on each object in the stream mapping it to its respective spot in the transformed stream using .map().
Next it uses .collect() to bring it back from a stream to a list using Collectors.toList().
If you are working with a lot of objects within myClassList, this process can be sped up using .parallelStream() instead of .stream(), but if you are not working with a large amount of data, you may see a reduction in performance with .parallelStream(). It all depends on how many objects you expect to be present within the List.
public interface Converter<I, O> {
void tranformer(List list);
O retriever(I obj);
}
_
public static <I, O> void transform(Converter<I, O> converter, List inputList) {
Iterator<I> it = inputList.iterator();
List list = new LinkedList<>();
while (it.hasNext()) {
list.add(converter.retriever(it.next()));
}
converter.tranformer(list);
}
_
public static void main(String[] args) {
List<MyClass> list = new ArrayList<>();
list.add(new myClass("John", 12);
list.add(new myClass("Mike", 16);
list.add(new myClass("Eric", 13);
list.add(new myClass("Mark", 142);
list.add(new myClass("Alex", 112);
MyClassCollection myclasscollection = new MyClassCollection(list);
final List collectionList = myclasscollection.collection();
CollectionUtils.transform(new Converter<myClass, String>() {
#Override
public void tranformer(List list) {
employeeList.clear();
employeeList.addAll(list);
}
#Override
public String retriever(myClass obj) {
return obj.name; // make the data member public or add getter
}
}, collectionList);
collectionList.get(0).toString.toLowerCase();
}
This isn't fully what you need but I bet this isn't bad alternative. Please, notice that could output collection collectionList will be collection of objects ( not String ), however, you can access to methods of the String data type just to right like this collectionList.get(0).toString.toLowerCase(); Hope this help.
I have a list of object Ob defined as
class Ob {
private String type;
private List<String> attire;
// standard getter and setters
public Ob (String type){
this.type=type;
}
public Ob addAttrire(String att){
if(attire == null){
attire = new ArrayList<>();
}
attire.add(att);
return this;
}
}
I receive objects as
[{
"type" : "upper"
attires : [{"t1","t2"}]
},
{
"type" : "upper"
attires : ["t3","t4"]
},
{
"type" : "lower"
attires : ["l1","l2"]
}]
which I have to combine as
[{
"type" : "upper"
attires : ["t1","t2","t3","t4"]
},{
"type" : "lower"
attires : ["l1","l2"]
}]
How can I use stream to do that. Does reduce help?
The stream one can use is
List<Ob> coll = new ArrayList<>();
coll.add(new Ob("a").addAttrire("1").addAttrire("2").addAttrire("3"));
coll.add(new Ob("a").addAttrire("1").addAttrire("2").addAttrire("3"));
coll.add(new Ob("a").addAttrire("1").addAttrire("2").addAttrire("3"));
coll.add(new Ob("b").addAttrire("1").addAttrire("2").addAttrire("3"));
coll.add(new Ob("b").addAttrire("1").addAttrire("2").addAttrire("3"));
coll.add(new Ob("b").addAttrire("1").addAttrire("2").addAttrire("3"));
Collection<Ob> values = coll.stream()
.collect(toMap(Ob::getType, Function.identity(), (o1, o2) -> {
o1.getAttire().addAll(o2.getAttire());
return o1;
})).values();
Updated the question with solution of Ruben. There is no requirement to remove duplicates, but it can be done using set in Ob for attire. The current solution worked flawlessly.
You could collect toMap with a merge function that merges the lists
Collection<Ob> values = coll.stream()
.collect(toMap(Ob::getType, Function.identity(), (o1, o2) -> {
o1.getAttire().addAll(o2.getAttire());
return o1;
})).values();
This solution uses the groupingBy collector and then a separate step that creates a new Ob which is the result of merging all the Obs that have the same type.
I think Rubens solution has an advantage over this answer because it's a little shorter and simpler. I think this answer has an advantage because it doesn't modify the original Obs and hence is more in a functional style.
public static void testTrickyStreamSet() {
Stream<Ob> s = Stream.of(
new Ob("a", "1", "2"),
new Ob("b", "1", "4"),
new Ob("a", "1", "3"),
new Ob("b", "1", "5"));
List<Ob> os = s.collect(groupingBy(o -> o.type))
.entrySet().stream()
.map(e -> new Ob(e.getKey(),
e.getValue().stream().flatMap(o -> o.attire.stream()).collect(toList())))
.collect(toList());
// Prints [<Ob type=a, attire=[1, 2, 3]>, <Ob type=b, attire=[1, 4, 5]>]
System.out.println(os);
}
public static class Ob {
public String type;
public List<String> attire;
public Ob(String type, String... attire) {
this.type = type;
this.attire = Arrays.asList(attire);
}
public Ob(String type, List<String> attire) {
this.type = type;
this.attire = new ArrayList<>(attire);
}
#Override
public String toString() {
return "<Ob type=" + type + ", attire=" + attire + ">";
}
}
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();
}
}
}