【问题标题】:Java 8 - sort a Collection of Objects by a property with an order defined in an arrayJava 8 - 按在数组中定义的顺序按属性对对象集合进行排序
【发布时间】:2016-12-27 10:43:28
【问题描述】:

假设我有以下课程:

public class Foo {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public Foo setId(int id) {
        this.id = id;
        return this;
    }

    public String getName() {
        return name;
    }

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

然后,我在Collection<Foo> fooCollection 中有几个Foo 对象和字符串数组String[] names

现在我想通过属性name 订购fooCollection,以相同的顺序排列name 字符串在names 中订购。

如何使用 Java 8 Stream 做到这一点?

【问题讨论】:

  • 这如何涉及names 数组?

标签: sorting java-8 java-stream


【解决方案1】:

您可以使用Arrays.asList(names).indexOf(f.getName()) 作为比较键:

List<String> nameList = Arrays.asList(names);
List<Foo> fooList = new ArrayList<>(fooCollection);
fooList.sort(Comparator.comparingInt(f -> nameList.indexOf(f.getName())));

或者,如果您确实想要 Stream API:

List<Foo> fooList = fooCollection.stream()
    .sorted(Comparator.comparingInt(f -> nameList.indexOf(f.getName())))
    .collect(Collectors.toList());

但是,如果你有很多名字,你可以考虑提高效率,首先准备一个名字的反向索引:

Map<String, Integer> nameMap = IntStream.range(0, names.length)
    .boxed()
    .collect(Collectors.toMap(idx -> names[idx], Function.identity()));
List<Foo> fooList = fooCollection.stream()
    .sorted(Comparator.comparing(f -> nameMap.get(f.getName())))
    .collect(Collectors.toList());

这里我假设所有的名字都是不同的,每个Foonames数组中都有对应的名字。

【讨论】:

  • 谢谢,我认为它的流 api 版本正好解决了我的问题!
【解决方案2】:

首先,您使用static factory methodsnames 数组构建Comparator,例如通过数组上的reduction。然后将比较器传递给stream.sort

【讨论】:

  • 你能给一个比较器的代码示例吗?不知道怎么做
  • @YanickNedderhoff 检查我发布的代码中的 cmets
【解决方案3】:

首先您需要将String[] 转换为List&lt;String&gt; 以利用indexOf 方法。然后使用Comparator&lt;T&gt; 接口比较新名称列表中Foo.name 的等效索引。我制作了整个代码,因此您可以复制并运行它:

package io.github.gabrielbb.java.utilities;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 *
 * @author Gabriel Basilio Brito
 * @since 27-12-16
 */
public class CollectionSortingBasedOnArray {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Donald", "Trump"));
        personList.add(new Person("Gabriel", "Basilio"));
        personList.add(new Person("Chris", "Brown"));
        personList.add(new Person("Barack", "Obama"));

        String[] sortedNames = {"Barack", "Chris", "Donald", "Gabriel"};
        List<String> sortedNamesList = Arrays.asList(sortedNames); // Converting Array to List to take advantage of "indexOf" method

        Collections.sort(personList, (Person p1, Person p2) -> { // Using the Comparator<T> interface with Lambda Expression
            Integer n1 = sortedNamesList.indexOf(p1.getName()); // Get the position of the first person name in the Names List (before an Array)
            Integer n2 = sortedNamesList.indexOf(p2.getName()); // Get the position of the second person name in the Names List
            return n1.compareTo(n2); // Now is simple: Just compare those indexes.
        });

        // Collections.sort lines can be replaced with: personList.sort(Comparator.comparingInt(p -> sortedNamesList.indexOf(p.getName())));

        personList.stream().forEach((p) -> {
            System.out.println(p.getName() + " " + p.getLastName()); // Print each Person Information
        });
    }
}

class Person {

    private String name;
    private String lastName;

    public Person(String name, String lastName) {
        this.name = name;
        this.lastName = lastName;
    }

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the lastName
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * @param lastName the lastName to set
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

}

Collections.sort 行是为了更好地理解背后发生的事情,但它可以替换为:

personList.sort(Comparator.comparingInt(p -> sortedNamesList.indexOf(p.getName())));

【讨论】:

    【解决方案4】:

    已经有一段时间了,但你可以缩短比较像

    List fooSortedByName = new ArrayList(fooCollection);
    fooSortedByName.sort(Comparator.comparing(Foo::getName));
    

    【讨论】:

    • 我认为这个答案没有回答所问的问题。请求是按照names 中的名称顺序对fooCollection 进行排序。
    • 你说得对,List.copyOf() 返回一个不可变列表,我将其更改为使用 ArrayList,现在它基本上是接受的答案 ;-)
    • 不是。接受的答案按与fooCollection 分开的名单排序。您只需按名称字段排序。
    猜你喜欢
    • 1970-01-01
    • 2018-04-19
    • 2018-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-11
    • 1970-01-01
    相关资源
    最近更新 更多