【问题标题】:Transformation after grouping operation using Collectors in java在java中使用收集器进行分组操作后的转换
【发布时间】:2021-02-06 17:42:29
【问题描述】:

我有一个简单的练习代码,其中有一个简单的 Person 对象列表。

我想做的是根据他们的年龄是奇数的偶数来划分他们,然后如果他们属于偶数组,则将他们的名字转换为大写,否则将名字转换为小写。

我面临的困难是使用 collect() 操作执行此转换。

这是简单的人类

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

@AllArgsConstructor
@Data
@Builder
@NoArgsConstructor
public class Person {

    private String name;

    private Gender gender;

    private int age;

    @Override
    public String toString() {
        return String.format("%s -- %s -- %d", name, gender, age);
    }

}

这是一个简单的 Person 对象列表

public static List<Person> getExpandedList() {
        return Arrays.asList(new Person("Sara", Gender.FEMALE, 20), new Person("Sara", Gender.FEMALE, 22),
                new Person("Bob", Gender.MALE, 20), new Person("Paula", Gender.FEMALE, 32),
                new Person("Paul", Gender.MALE, 31), new Person("Jack", Gender.MALE, 2),
                new Person("Jack", Gender.MALE, 72), new Person("Jill", Gender.FEMALE, 12),
                new Person("Mahi", Gender.FEMALE, 11), new Person("Natalie", Gender.FEMALE, 3));
    }

以下是我尝试用于转换的代码。

所以我希望 Mahi、Natalie 和 Paul 的名字全部小写,其余的名字全部大写,我希望整个人都返回对象,而不仅仅是名字。

这是我正在尝试的代码

import java.util.List;
import java.util.Map;

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

public class PartitioningWithCollectors {

    public static void main(String[] args) {
        List<Person> personList = StreamsPracticeUtil.getExpandedList();

 
        Map<Boolean, List<Person>> updatedPersons = personList.stream()
                .collect(partitioningBy(person -> person.getAge() % 2 == 0,
                        //filtering(person -> person.getAge() % 2 == 0,
                        mapping(person -> Person.builder()
                                .age(person.getAge())
                                .gender(person.getGender())
                                .name(person.getName().toUpperCase())
                                .build(), toList())));//);

        System.out.println(updatedPersons);
    }

}

当然,问题是一旦我使用任何类型的过滤,分区的其他结果就消失了。

实际上我想以一种功能性的方式执行以下操作

for (Person person : personList) {
            if (person.getAge() % 2 == 0) {
                person.setName(person.getName().toUpperCase());
            } else {
                person.setName(person.getName().toLowerCase());
            }
        }

        System.out.println(personList);

感谢我能得到的任何帮助。谢谢..!!

【问题讨论】:

    标签: java java-stream collect


    【解决方案1】:

    您可以在.collect();之前映射每个Person

    例如:

    personList.stream()
                    .map(SET_NAME_CASE)
                    .collect(.....)
    

    当 SET_NAME_CASE 是您的自定义函数时:

    public static final UnaryOperator<Person> SET_NAME_CASE =
                source -> {
                        if(person.getAge % 2 == 0){
                           return Person.builder()                            
                                    .name(person.getName().toUpperCase())
                                    .build();
                        } else {
                           return Person.builder()                            
                                    .name(person.getName().toLowerCase())
                                    .build();
                        }
    };
    

    之后,您将拥有一个带有集合名称的 Person 对象。 您可以进行任何分组,也可以设置其他字段 Person。 将您的 @Builder 更改为 @Builder(toBuilder = true),您将在流中添加任何映射并设置其他字段,例如:

      personList.stream()
                        .map(SET_NAME_CASE)
                        .map(
                             source -> Person.toBuilder()
                                        .age(person.getAge())
                                        .gender(person.getGender())
                                        .build()
                         )
                        .collect(.....)
    

    【讨论】:

    • 感谢您的回复。看起来我试图做的对于收集提供给我们的东西来说有点太多了。似乎这是唯一的方法(将条件检查和转换与流操作分开)我可以分区和处理这两个部分而无需再次迭代。感谢您的建议...!!
    【解决方案2】:

    您可以使用Collectors.partitioningBy 内的下游收集器Collectors.mapping 来实现此目的。当然,Collectors.mapping 需要Collectors.toList 来定义最终输出。

    我还建议提取测试年龄是否偶数的谓词,将在第一次分区时重复使用,然后用三元运算符映射值。

    以下代码已准备好运行:

    List<Person> personList = Arrays.asList(new Person("Sara", Gender.FEMALE, 20), 
        new Person("Sara", Gender.FEMALE, 22),
        new Person("Bob", Gender.MALE, 20), new Person("Paula", Gender.FEMALE, 32),
        new Person("Paul", Gender.MALE, 31), new Person("Jack", Gender.MALE, 2),
        new Person("Jack", Gender.MALE, 72), new Person("Jill", Gender.FEMALE, 12),
        new Person("Mahi", Gender.FEMALE, 11), new Person("Natalie", Gender.FEMALE, 3));
    
    Predicate<Person> isAgeEven = person -> person.getAge() % 2 == 0;
    
    Map<Boolean, List<Person>> updatedPersons = personList
        .stream()
        .collect(Collectors.partitioningBy(
            isAgeEven,                                       // partition by the age
            Collectors.mapping(
                person -> isAgeEven.test(person) ?           // is odd or even?
                    person :                                 // even aged person is unchanged
                    new Person(                              // a constructor/builder
                            person.getName().toUpperCase(),  // make the name uppercased
                            person.getGender(), 
                            person.getAge()),
                Collectors.toList())));                      // pack the values to a list
    
    updatedPersons.forEach((k,v) -> System.out.println(k + " " + v));
    
    false [PAUL -- MALE -- 31, MAHI -- FEMALE -- 11, NATALIE -- FEMALE -- 3]
    true [Sara -- FEMALE -- 20, Sara -- FEMALE -- 22, Bob -- MALE -- 20, Paula -- FEMALE -- 32, Jack -- MALE -- 2, Jack -- MALE -- 72, Jill -- FEMALE -- 12]
    

    或者,考虑到设置器,这个映射函数是可能的:

    person -> {
        if (isAgeEven.test(person)) {
            person.setName(person.getName().toUpperCase());
        } return person;
    }
    

    【讨论】:

    • 感谢您的回复。实际上,我写的代码可能有点误导。您分享的内容肯定会给我偶数年龄的大写名字,但我也想让奇数年龄的名字全部小写。 (它们目前处于初始化上限)。我认为我们可以添加该逻辑'?人:'在这里。
    【解决方案3】:

    如果我理解你的问题,那么你需要使用:

    Map<Boolean, List<Person>> updatedPersons = personList.stream()
            .collect(partitioningBy(person -> person.getAge() % 2 == 0,
                    mapping(PartitioningWithCollectors::updatePerson, toList())));
    

    其中updatePerson是一种将名称更改为大写或小写的方法:

    private static Person updatePerson(Person person) {
        String name = person.getName();
        if (person.getAge() % 2 == 0) {
            person.setName(name.toUpperCase());
        } else {
            person.setName(name.toLowerCase());
        }
        return person;
    }
    

    但是:我会在你的问题中使用最后一个解决方案,它更具可读性。

    【讨论】:

    • 感谢您的回复。这肯定会奏效。想知道是否有办法处理集合中的两个分区。我想我试图对收集做太多事情,似乎这是我可以分区和处理这两个部分而不必再次迭代的唯一方法。谢谢...!!
    猜你喜欢
    • 1970-01-01
    • 2018-02-14
    • 1970-01-01
    • 1970-01-01
    • 2016-01-25
    • 2021-07-23
    • 1970-01-01
    • 2011-02-15
    • 2018-03-26
    相关资源
    最近更新 更多