我有一个未排序字符串列表,其中条目是{A,B,C,D}
:
List<String> strings = new ArrayList<>(Arrays.asList("A","C","B","D","D","A","B","C","A","D","B","D","A","C"));
我需要排序/(组)他们在一个自定义的顺序采取一个项目的时间有一个结果,如:
[A, B, C, D, A, B, C, D, A, B, C, D, A, D]
我正在努力想出一个如何做到这一点的主意。任何帮助吗?
我尝试使用自定义Comparator<String>
,但无法实现first A < second A
和first D < second A
的逻辑。
也试过Stream. groupingBy
:
Collection<List<String>> coll = strings.stream().collect(Collectors.groupingBy(s -> s)).values();
将相同的字符串分组。
[[A, A, A, A], [B, B, B], [C, C, C], [D, D, D, D]]
但是我不确定如何一次从上面的列表中取一个元素,直到没有元素可用。有人能告诉我怎么做吗?需要一个正确方向的提示。
构建一个全新的列表可能会导致一些其他的解决方案,例如:
Map<String, Long> counts = strings.stream().collect(groupingBy(identity(), TreeMap::new, counting()));
List<String> ordered = new ArrayList<>();
while (!counts.isEmpty()) {
for (Iterator<Map.Entry<String, Long>> it = counts.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Long> entry = it.next();
ordered.add(entry.getKey());
long newCount = entry.getValue() - 1;
if (newCount == 0) {
it.remove();
} else {
entry.setValue(newCount);
}
}
}
其中strings
为输入列表,ordered
为输出列表。
为每个值添加数字前缀,排序并删除前缀,有限制的数组大小不能远远大于数字前缀
List<String> strings = new ArrayList<>(Arrays.asList("A","C","B","D","D","A","B","C","A","D","B","D","A","C"));
Map<String, Integer> m = new HashMap<>();
strings.stream()
.map(i -> String.format("%dx%s", (100000 + m.merge(i, 1, (n, w) -> n+w)), i))
.sorted()
.map(i -> i.replaceFirst("^\d+x", ""))
.collect(Collectors.toList());
这与sp00m的答案大致相同,但使用两个流实现:
Map<String, Long> groups = strings.stream()
.collect(Collectors.groupingBy(Function.identity(),
TreeMap::new,
Collectors.counting()));
List<String> result = IntStream.range(0, groups.values().stream()
.mapToInt(Long::intValue).max().orElseThrow())
.mapToObj(c -> groups.keySet().stream().filter(k -> groups.get(k) > c))
.flatMap(Function.identity())
.collect(Collectors.toList());
排序由TreeMap
负责。只要确保您的实际列表元素是可比较的(或者您提供了正确的TreeMap供应商)
嗯,这里有另一种方法。
首先,我们可以得到一个包含所有项目的列表的列表,就像你在问题中描述的那样。
[
[A, A, A, A],
[B, B, B],
[C, C, C],
[D, D, D, D]
]
Collection<List<String>> chunks = strs.stream()
.collect(Collectors.groupingBy(Function.identity(), TreeMap::new, Collectors.toList()))
.values();
您可以通过将TreeMap::new
替换为() -> new TreeMap<>(comparator)
来插入自定义Comparator
。
那么我们可以用它来得到所有的ABCD
组。
IntStream.iterate(0, i -> i + 1)
.mapToObj(i -> chunks.stream()
.map(sublist -> i < sublist.size() ? sublist.get(i) : null)
.filter(Objects::nonNull)
.toList())
.takeWhile(list -> !list.isEmpty())
.forEach(System.out::println);
这里的情况是,我们循环遍历每个子列表并取第一个元素,然后取每个第二个元素,以此类推
如果你把获取一堆列表的特定索引的代码放到一个单独的方法中,这会变得更好读:
public static <T> Stream<T> nthElement(Collection<? extends List<T>> list, int index) {
return list.stream()
.map(sublist -> index < sublist.size() ? sublist.get(index) : null)
.filter(Objects::nonNull);
}
IntStream.iterate(0, i -> i + 1)
.mapToObj(i -> nthElement(chunks, i).toList())
.takeWhile(list -> !list.isEmpty())
.forEach(System.out::println);
不那么优雅,但更清楚正在发生什么。您可以在每个字符串前放置一个整数,表示遇到该字符串值的次数。然后正常排序并使用正则表达式替换整数值。
public static void main(String[] args) {
List<String> strings = new ArrayList<>(Arrays.asList("A","C","B","D","D","A","B","C","A","D","B","D","A","C"));
List<String> sortableString = stringTransform(strings);
sortableString.stream().sorted().forEach(s -> System.err.print(s.replaceAll("[0-9]", "")));
}
private static List<String> stringTransform(List<String> stringList) {
Map<String, Integer> stringMap = new HashMap<>();
List<String> result = new ArrayList<>();
for (String string : stringList) {
Integer integer = stringMap.get(string);
if (integer == null) {
integer = 0;
} else {
integer++;
}
stringMap.put(string, integer);
result.add(integer + string);
}
return result;
}