我有两个包含姓名的列表。第一个列表
l1
有 selected 个名称,第二个列表 l2
有 all 个名称。
示例:
List<String> l1 = {"en", "cu", "eu"};
List<String> l2 = {"ch", "cu", "en", "eu", "pe"};
l1
中的所有元素始终存在于l2
中。
我想获得将所有常用名称放在开头,然后是其余名称的输出,并且我需要将它们按升序排序。 像这样:
output -> {cu, en, eu, ch, pe};
我正在尝试做的事情已在下面显示,但不知道如何将公共元素放置在起始位置(第二个列表已排序)并将剩余元素放置在公共元素之后
List<String> common = l2.stream().filter(l2::contains).collect(Collectors.toList());
如果我理解正确,您希望通过将第一个列表
l2
中存在的值放在开头来对第二个列表l1
中的元素进行排序,并且这两部分都应按字母顺序排序。
您需要为此目的定义一个自定义
Comparator
。从 Java 8 开始,创建比较器的推荐方法是使用静态工厂方法,例如 Comparator.comparing()
。
在实现比较器时,首先我们需要检查第一个列表中是否存在特定值,并根据检查结果对元素进行排序(提醒:
boolean
值的自然排序是false
-> true
)。由于列表上的 contains()
成本很高(它会在后台对列表进行迭代,因此运行时间为 O(n)),因此将第一个列表中的数据转储到 HashSet
中会提高性能。并针对该集合执行检查。
Comparator.thenComparing()
并提供 Comparator.naturalOrder()
作为参数来链接第二个比较器。
具体实施方式如下:
List<String> l1 = List.of("en", "cu", "eu");
List<String> l2 = List.of("ch", "cu", "en", "eu", "pe");
// output -> {cu,en,eu,cu,pe};
Set<String> toCheck = new HashSet<>(l1);
Comparator<String> comparator =
Comparator.<String,Boolean>comparing(toCheck::contains).reversed()
.thenComparing(Comparator.naturalOrder());
List<String> sorted = l2.stream()
.sorted(comparator)
.toList(); // for Java 16 or collect(Collectors.toList())
System.out.println(sorted);
输出:
[cu, en, eu, ch, pe]
Set<String> selectedNames = Set.copyOf(l1);
List<String> sorted = Stream.concat(
l1.stream().sorted(),
l2.stream().filter(Predicate.not(selectedNames::contains)).sorted()
).toList();
这首先将选定的名称复制到
Set
中,以受益于更快的 contains
操作(O(1) 与 O(n))。
从那里开始,它结合了两个元素流。首先,仅对第一个列表中的元素进行排序。其次,仅对第二个列表中不在第一个列表中的元素进行排序。最后,它将组合后的
Stream
转换为 List
。