【问题标题】:split List of values based on value根据值拆分值列表
【发布时间】:2018-02-22 20:27:00
【问题描述】:

我有一个对象列表(在本例中为 Person),我想根据值将它们拆分为列表 Person 对象列表。在下面给出的示例中,我有一个 Person 对象,其中包含 name、id 和 Address 对象。地址对象有一个门牌号,街道 ID。现在我想使用集合 API 根据地址对象中的街道 ID 拆分它们。我尝试分组,按集合进行分区,但无法正常工作。我只想使用 Java 8。没有第三方。

预期结果:

[ [person1, person2, person3] , [person4, person5], [person6] ]

谢谢。

package testapplication2;

import java.util.ArrayList;
import java.util.List;

/**
 *
 *
 */
public class JavaCollections {

  public static void main(String[] args) {
    JavaCollections c = new JavaCollections();
    c.test1();
  }

  public void test1() {
    List<Person> persons = new ArrayList<>();
    Address address1 = new Address(1, "X Street", 100);
    Address address2 = new Address(2, "X Street", 100);
    Address address3 = new Address(3, "X Street", 100);
    Address address4 = new Address(4, "Y Street", 101);
    Address address5 = new Address(5, "Y Street", 101);
    Address address6 = new Address(6, "Z Street", 102);
    persons.add(new Person(1, "P1", address1));
    persons.add(new Person(2, "P2", address2));
    persons.add(new Person(3, "P3", address3));
    persons.add(new Person(4, "P4", address4));
    persons.add(new Person(5, "P5", address5));
    persons.add(new Person(6, "P6", address6));
  }

  public class Person {

    public int personId;
    private String name;

    private Address address;

    public Person() {

    }

    public Person(int personId, String name, Address address) {
      super();
      this.personId = personId;
      this.name = name;
      this.address = address;
    }

    public int getPersonId() {
      return personId;
    }

    public void setPersonId(int personId) {
      this.personId = personId;
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

    public Address getAddress() {
      return address;
    }

    public void setAddress(Address address) {
      this.address = address;
    }

    @Override
    public String toString() {
      return "Person{" + "personId=" + personId + ", name=" + name + ", address=" + address + '}';
    }

  }

  public class Address {

    public Address() {

    }

    public Address(int houseNumber, String streetName, int streetId) {
      this.houseNumber = houseNumber;
      this.streetId = streetId;
      this.streetName = streetName;
    }

    private int houseNumber;
    private String streetName;
    private int streetId;

    public int getHouseNumber() {
      return houseNumber;
    }

    public void setHouseNumber(int houseNumber) {
      this.houseNumber = houseNumber;
    }

    public String getStreetName() {
      return streetName;
    }

    public void setStreetName(String streetName) {
      this.streetName = streetName;
    }

    public int getStreetId() {
      return streetId;
    }

    public void setStreetId(int streetId) {
      this.streetId = streetId;
    }

    @Override
    public String toString() {
      return "Address{" + "houseNumber=" + houseNumber + ", streetName=" + streetName + ", streetId=" + streetId + '}';
    }

  }
}

【问题讨论】:

  • 在我看来,从概念上讲,您基本上想要一些与 flatMap 相反的东西

标签: java collections


【解决方案1】:

应该这样做:

    List< List<Person> > groups = new ArrayList<>( persons.stream().collect( 
    Collectors.groupingBy( p -> p.getAddress().getStreetId() ) ).values() );

或者@shmosel:

    List< List<Person> > groups = persons.stream().collect( Collectors.collectingAndThen(
    Collectors.groupingBy( p -> p.getAddress().getStreetId() ), 
    m -> new ArrayList<>( m.values() ) ) );

【讨论】:

  • 这不会创建Map&lt;Integer, List&lt;Person&gt;&gt; 吗? (更正的密钥类型。)我认为 OP 希望它像 List&lt;List&lt;Person&gt;&gt;
  • 我的意思是,我认为Map 可能比嵌套的Lists 更可取,但 OP 想要 OP 想要的东西。 :P(赞成。)
  • 或者,collectingAndThen(groupingBy(p -&gt; p.getAddress().getStreetId()), m -&gt; new ArrayList&lt;&gt;(m.values()))
  • 当然@shmosel。我也会将这种方法添加到答案中。
【解决方案2】:

满足您要求的解决方案是使用stream().collect() 并在person.address.streetId 字段上调用groupingBy 作为键。

这段代码应该可以做到:

final List<List<Person>> groupedPersons = persons.stream()
        .collect(Collectors.groupingBy(o -> o.address.streetId))
        .entrySet().stream()
        .map(Map.Entry::getValue).collect(Collectors.toList());
// Code for printing out
groupedPersons.forEach(people -> {
    System.out.print("[");
    System.out.print(people.stream().map(person -> String.format("person%d", person.personId)).collect(Collectors.joining(",")));
    System.out.print("]");
});

这是一个可以运行的完整示例:

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Partitions {

    public static void main(String[] args) {
        List<Person> persons = new ArrayList<>();
        Address address1 = new Address(1, "X Street", 100);
        Address address2 = new Address(2, "X Street", 100);
        Address address3 = new Address(3, "X Street", 100);
        Address address4 = new Address(4, "Y Street", 101);
        Address address5 = new Address(5, "Y Street", 101);
        Address address6 = new Address(6, "Z Street", 102);
        persons.add(new Person(1, "P1", address1));
        persons.add(new Person(2, "P2", address2));
        persons.add(new Person(3, "P3", address3));
        persons.add(new Person(4, "P4", address4));
        persons.add(new Person(5, "P5", address5));
        persons.add(new Person(6, "P6", address6));
        final List<List<Person>> groupedPersons = persons.stream()
            .collect(Collectors.groupingBy(o -> o.address.streetName))
            .entrySet().stream()
            .map(Map.Entry::getValue).collect(Collectors.toList());
        groupedPersons.forEach(people -> {
            System.out.print("[");
            System.out.print(people.stream().map(person -> String.format("person%d", person.personId)).collect(Collectors.joining(",")));
            System.out.print("]");
        });

    }

    public static class Person {

        public int personId;
        private String name;

        private Address address;

        public Person() {

        }

        public Person(int personId, String name, Address address) {
            super();
            this.personId = personId;
            this.name = name;
            this.address = address;
        }

        public int getPersonId() {
            return personId;
        }

        public void setPersonId(int personId) {
            this.personId = personId;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Address getAddress() {
            return address;
        }

        public void setAddress(Address address) {
            this.address = address;
        }

        @Override
        public String toString() {
            return "Person{" + "personId=" + personId + ", name=" + name + ", address=" + address + '}';
        }

    }

    public static class Address {

        private int houseNumber;
        private String streetName;
        private int streetId;
        public Address() {

        }
        public Address(int houseNumber, String streetName, int streetId) {
            this.houseNumber = houseNumber;
            this.streetId = streetId;
            this.streetName = streetName;
        }

        public int getHouseNumber() {
            return houseNumber;
        }

        public void setHouseNumber(int houseNumber) {
            this.houseNumber = houseNumber;
        }

        public String getStreetName() {
            return streetName;
        }

        public void setStreetName(String streetName) {
            this.streetName = streetName;
        }

        public int getStreetId() {
            return streetId;
        }

        public void setStreetId(int streetId) {
            this.streetId = streetId;
        }

        @Override
        public String toString() {
            return "Address{" + "houseNumber=" + houseNumber + ", streetName=" + streetName + ", streetId=" + streetId + '}';
        }

    }
}

这将打印出来:

[person4,person5][person6][person1,person2,person3]

更新:

如果您想保留它添加到初始列表中的顺序,您可以提供一个收集器,该收集器提供 java.util.LinkedHashSet - 一种保留唯一性和原始插入顺序的数据结构。

分组代码如下所示:

final List<Set<Person>> groupedPersons = persons.stream()
        .collect(Collectors.groupingBy(o -> o.address.streetId, 
                Collector.of(() -> new LinkedHashSet<Person>(), HashSet::add, (s1, s2) -> {
            s1.addAll(s2);
            return s1;
        })))
        .entrySet().stream()
        .map(Map.Entry::getValue).collect(Collectors.toList());

如果您在上面的示例中使用此代码,它将打印出来:

[person1,person2,person3][person4,person5][person6]

【讨论】:

  • 是否可以保持顺序(街道 id 的升序)或添加到初始列表中的顺序?在此示例中,人员按街道 ID 升序添加。拆分后,打印时,显示顺序不同。只是想知道是否有有序列表类型的 API。没有答案,没有问题。
  • 是的,您可以保留添加条目的原始顺序。请检查更新的答案。
【解决方案3】:

要做到这一点,你需要

  • 使用列表中的Stream
  • 然后groupByidAddress,所有具有相同adressId的Person都将在一个Map&lt;Integer,List&lt;Person&gt;&gt;
  • 然后你会得到values(所有List&lt;Person&gt; 并将它们收集在一起)
public static void main(String[] args) {
    JavaCollections c = new JavaCollections();
    List<Person> persons = c.test1();
    List<List<Person>> res = new ArrayList<>(persons .stream()
        .collect(Collectors.groupingBy(o -> o.getAddress().getStreetId())).values());
    System.out.println(test);
 }

并将test1()更改为

public static List<Person> test1() {
    ...; 
    return persons;
} 

返回列表

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-29
    • 1970-01-01
    相关资源
    最近更新 更多