0

I have a list of class A and class B with no duplicate elements. "code" attribute will be same across both class A and B. I want to convert them to Map<String, C> using java 8 streams. Please help

public class A {
    private String code;
    private boolean status;
    field 3...
    field 4..

 
}

public class B {
    private String code;
    private String location;
    field 5...
    field 5..
}

public class C {
    private String location;
    private boolean status;
}

List1 : [A(code=NY, status=false), A(code=NJ, status=true),A(code=TX, status=true), A(code=NM, status=false)]

List2 : [B(code=NY, location=NewYork), B(code=NJ, location=NewJersey),B( code=TX, location=Texas), B(code=NM, location=NewMexico)]

Map =  map{NY=C(location=NewYork, status=false), NJ=C(location=NewJersey, status=true), TX=C(location=Texas, status=true),NM=C(location=NewMexico, status=false)}


Final map should be in the same order as the elements in List1

~A

UPDATE : i updated the code to below, but its not compiling. any idea about whats wrong?

package test;

import java.util.*;
import java.util.stream.Collectors;

public class Test {

    static void main(String[] args){


        new Test().testFunction();

    }

      void testFunction(){


        System.out.println("Hello World");



        List<A> listA = Arrays.asList(new A("NY", false), new A("NJ", true), new A("TX", false), new A("AZ", true));
        List<B> listB = Arrays.asList(new B("NY", "New York"), new B("NJ", "New Jersey"),
                new B("TX", "Texas"), new B("NM", "New Mexico"));


        Map<String, B> mapB = listB
                .stream()
                .collect(Collectors.toMap(B::getCode, b -> b, (b1, b2) -> b1));

        Map<String, C> innerJoin = listA
                .stream()
                .filter(a -> mapB.containsKey(a.getCode())) // make sure a B instance exists with the code
                .map(a -> Map.entry(a.getCode(), new C(mapB.get(a.getCode()).getLocation(), a.getStatus())))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> c1, HashMap::new));

        innerJoin.forEach((code, c) -> System.out.println(code + " -> " + c));

    }

}


     class A {
        private String code;
        private boolean status;

         public A(String code, boolean status) {
             this.code = code;
             this.status = status;
         }
         public String getCode() {
             return this.code;
         }
         public boolean getStatus() {
             return this.status;
         }
     }

     class B {
        private String code;
        private String location;

         public B(String code, String location) {
             this.code = code;
             this.location = location;
         }

         public String getCode() {
             return this.code;
         }
         public String getLocation() {
             return this.location;
         }
     }

     class C {
        private String location;
        private boolean status;

         public C(String location, boolean status) {
             this.location = location;
             this.status = status;
         }
         public String getLocation() {
             return this.location;
         }
         public boolean getStatus() {
             return this.status;
         }
    }

1
  • 1
    share what you have tried so far so that others get to understand "how" to help you. Commented Jan 28, 2021 at 8:04

4 Answers 4

2

You should use somethins like:

Map<String, B> mapList2 = list2.stream().collect(Collectors.toMap(B::getCode, 
    Function.identity()); // create temporary map

Map<String,B> result = list1.stream().collect(
            LinkedHashMap::new,
            (map, a) -> map.put(
                   a.getCode(), // get code from A
                   new C(
                           mapList2.get(a.getCode()).getLocation() // get location from B
                           a.getStatus() // get status from A
                   ),  
             Map::putAll); // use LinkedHashMap to keep order       
Sign up to request clarification or add additional context in comments.

5 Comments

Agree that your solution is what OP exactly asked for. However it makes no sense for such a simple task to iterate 2 times when only 1 is needed and make a super complex code when it can be much easier and lighter. Just my 2 cents.
@PanagiotisBougioukos, do you mean one iteration seems is possible in case the lists are "sorted" by code property?
@Alex Rudenko check my answer
@Panagiotis Bougioukos, do you know that contains and get in List iterate all collection every time, so you solution iterate list1.size() * list2.size() * 2 instead of list1.size() + list2.size(). For example, list1.size() = 1000, list2.size() = 1000, your solution has iteration 2 000 000 instead of 2000. Are you sure it is super complex code when it can be much easier and lighter? :)
Your points are right. I have not analyzed it so much when I wrote it.
1
Map<String, C> map = IntStream.range(0, list1.size())
    .mapToObj(i -> new CodeIndex(list1.get(i).code(), i))
    .collect(Collectors.toMap(
        CodeIndex::code,
        ci -> new C(list2.get(ci.index()).location(), list1.get(ci.index()).status()),
        (a, b) -> a,
        LinkedHashMap::new
    ));

What happens is that we first get all valid indexes of both lists, then we map to a (custom) container class holding both the index and the code. (I called it CodeIndex, but you may as well use Map.Entry or some Pair class.)

At last, we collect it into a LinkedHashMap (because it retains insertion order), mapping the keys to simply CodeIndex::code and the values to new C(«location_from_list2», «status_from_list1»).

Note that this code traverses the lists once.

Of course, this assumes that:

  • both lists have the same codes;
  • all codes of both list1 and list2 are in the same order.

Note that I used record-style getters, that is, getters with the same name as their corresponding field names. For example, location() instead of getLocation().

Comments

1

This task seems like to join two lists of objects by one key property. Taking into account the requirements, it should be like inner join with the help of temporary map created from the listB.

Map<String, B> mapB = listB
        .stream()
        .(Collectors.toMap(B::getCode, b -> b, (b1, b2) -> b1)); // use merge function as a defense measure

Map<String, C> innerJoin = listA
        .stream()
        .filter(a -> mapB.containsKey(a.getCode())) // make sure a B instance exists with the code
        .map(a -> Map.entry(a.getCode(), new C(mapB.get(a.getCode()).getLocation(), a.isStatus())))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> c1, LinkedHashMap::new));

Test:

List<A> listA = Arrays.asList(new A("NY", false), new A("NJ", true), new A("TX", false), new A("AZ", true));
List<B> listB = Arrays.asList(new B("NY", "New York"), new B("NJ", "New Jersey"), new B("TX", "Texas"), new B("NM", "New Mexico"));

// ...
innerJoin.forEach((code, c) -> System.out.println(code + " -> " + c));

Output:

NY -> C(location=New York, status=false)
NJ -> C(location=New Jersey, status=true)
TX -> C(location=Texas, status=false)

The "simplest" solution may be based on assumption that the items in both lists are "ordered" by the same code field, then no temporary map is needed:

Map<String, C> simple = IntStream
        .range(0, listA.size())
        .mapToObj(i -> Map.entry(listA.get(i).getCode(), new C(listB.get(i).getLocation(), listA.get(i).isStatus())))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> c1, LinkedHashMap::new));

However, such solution is too fragile.

Comments

1

I think you have to use something like this to really maintain the order and group.

List<A> aList = Arrays.asList(new A("NY", false), new A("NJ", true));
List<B> bList = Arrays.asList(new B("NY", "NewYork"), new B("NJ", "NewJersey"));

LinkedHashMap<String, A> mapA =
        aList.stream().collect(
                Collectors.toMap(
                        A::getCode,
                        a -> a,
                        (e1, e2) -> e1,
                        LinkedHashMap::new));

LinkedHashMap<String, B> mapB =
        bList.stream().collect(
                Collectors.toMap(
                        B::getCode,
                        b -> b,
                        (e1, e2) -> e1,
                        LinkedHashMap::new));

Set<String> keys = new LinkedHashSet<>(mapA.keySet());
keys.addAll(mapB.keySet());

LinkedHashMap<String, C> result2 = keys.stream().collect(
        Collectors.toMap(
                key -> key,
                key -> new C(
                        mapB.get(key).getLocation(),
                        mapA.get(key).isStatus()),
                (e1, e2) -> e1,
                LinkedHashMap::new));
System.out.println(result2);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.