'Java 8 - Best way to separate a list into 3 different lists

Input:

  1. I have a List abcLines which has all types of lines of type A or B or C.
  2. I also have a readymade List of type A (aLines).

I want to separate the big list (abcLines) into 3 different lists (aLines, bLines and cLines).

class ABCLine {
  int id;
  Object tlId; // value of this field is false (if the line type is A or C) or the value of this field is a List (if the type is B)
}


    List<ABCLine> bLines = new ArrayList<ABCLine>();
    List<ABCLine> cLines = new ArrayList<ABCLine>();
    
    abcLines.forEach(abcLine -> {
        if (abcLine.tlId instanceof List) {
            bLines.add(abcLine);
            abcLines.remove(abcLine);
        } else {
            aLines.forEach(aLine -> {
               if (aLine.id.equals(abcLine.id)) {
                   abcLines.remove(abcLine);                       
               }
            });
        }
    });
    
    cLines = abcLines;

How can I write this code using Java 8 streams in a better way?



Solution 1:[1]

Here's a solution that uses Collectors.partitioningBy to separate the lines.

    List<ABCLine> aLines = new ArrayList<>(); //Ready made aline list
    List<ABCLine> abcLines = new ArrayList(); // list that contains the A B and C lines.

    //1. We first separate the b lines from the a and c lines by checking whether the field `tlId` of an instance is a `List` or not. We use `Collectors.partitioningBy` to partition the instances based on this condition. The end result is a map holding two lists that can be retrieved with the keys `true` and `false`.
    Map<Boolean, List<ABCLine>> partitionedLines = abcLines.stream().collect(Collectors.partitioningBy(abcLine -> abcLine.tlId instanceof List));

    //2. Retrieve the B lines from the map.
    List<ABCLine> separatedBLines = partitionedLines.get(true);
    
    //3. Retrieve the A and C lines from the map.
    List<ABCLine> aAndCLines = partitionedLines.get(false);

    //4. Make a Set containing all the `id` field values from the ready made aline list that you mentioned. This Set will be used to partition the a lines from the c lines. 
    Set<Integer> aLineIds = aLines.stream().map(aLine -> aLine.id).collect(Collectors.toSet());

    //5. We use `Collectors.partitioningBy` again to partition/separate the A lines from the C lines using `aLineIds.contains(line.id)` as the condition. Because the type of the aList id collection is a set we can determine very fast whether the `id` of `line` equals any of the ids in the set.
    Map<Boolean, List<ABCLine>> aAndCMap = aAndCLines.stream().collect(Collectors.partitioningBy(line -> aLineIds.contains(line.id)));

    //6. Now you only have to retrieve the A and C lines from the map and you're done.
    List<ABCLine> separatedALines = aAndCMap.get(true);
    List<ABCLine> separatedCLines = aAndCMap.get(false);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1