简体   繁体   中英

Mapping fields of List to fields of a single Object

Suppose, I have a List of cats like this:

[Cat[name="Minnie", age=3], Cat[name="Pixie", age=1], Cat[name="Kazy", age=5]]

And an Object Cats with fields:

class Cats {
int MinnieAge;
int PixieAge;
int KazyAge;
}

What is the best way to map the ages to this Object? Preferably avoiding imperative approach and keeping it nice and clean. Is this possible with MapStruct or the streams API?

If you want to use streams you can start from implementing a collector:

public class CatsCollector implements Collector<Cat, Cats, Cats> {

    @Override
    public Supplier<Cats> supplier() {
        return () -> new Cats();
    }

    @Override
    public BiConsumer<Cats, Cat> accumulator() {
        return (cats1, cat) -> {
            switch (cat.name){
                case "Minnie": cats1.minnieAge = cat.age; break;
                case "Pixie": cats1.pixieAge = cat.age; break;
                case "Kazy": cats1.kazyAge = cat.age; break;
            }
        };
    }

    @Override
    public BinaryOperator<Cats> combiner() {
        return (cats1, cats2) -> cats1;
    }

    @Override
    public Function<Cats, Cats> finisher() {
        return cats1 -> cats1;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Set.of(Characteristics.UNORDERED);
    }
}

PS - Here a combiner is more like a stub because there are no requirements for combining logic provided.

PPS - There are also sort of assumptions to simplify access modifiers stuff.

Then if you have the model like this:

public class Cats {
    int minnieAge;
    int pixieAge;
    int kazyAge;

    @Override
    public String toString() {
        return "Cats{" +
                "minnieAge=" + minnieAge +
                ", pixieAge=" + pixieAge +
                ", kazyAge=" + kazyAge +
                '}';
    }
}

You can use your custom collector:

public static void main(String[] args) {
    List<Cat> cats = List.of(
            new Cat("Minnie", 3), 
            new Cat("Pixie", 1), 
            new Cat("Kazy", 5));
    Cats catsResult = cats.stream().collect(new CatsCollector());
    System.out.println(catsResult);
}

I would define a Cat class with the attribute name and age. Then simply parse the input string into cat objects and store them in a list which already supports stream. See below example:

import java.util.*;
class Cat {
    public String name;
    public int age;
    private Cat(String name, int age) { this.name = name; this.age = age;}
    public String toString(){
        return String.format("Cat[name=\"%s\", age=%d]",name,age);
    }
    public static List<Cat> parse(String input) {
        List cats = new ArrayList();
        int i = 0;
        while (true) {
            i = input.indexOf("name=", i);
            if (i < 0) break;
            String name = input.substring(i + 6, input.indexOf("\"", i + 7));
            i = input.indexOf("age=", i);
            int age = Integer.parseInt(input.substring(i + 4, input.indexOf("]", i)));
            cats.add(new Cat(name, age));
        }
        return cats;
    }
}
public class Main{
    public static void main(String[] args) {
        String input = "[Cat[name=\"Minnie\", age=3], Cat[name=\"Pixie\", age=1], Cat[name=\"Kazy\", age=5]]";
        List<Cat> cats = Cat.parse(input);
        cats.forEach(System.out::println);
        cats.stream().filter(e->e.age > 4).forEach(System.out::println);
    }
}

Output:

run:
Cat[name="Minnie", age=3]
Cat[name="Pixie", age=1]
Cat[name="Kazy", age=5]
Cat[name="Kazy", age=5]
BUILD SUCCESSFUL (total time: 0 seconds)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM