假设我有一个列表
ArrayList<String> arr = new ArrayList(Arrays.asList("N1", "N2", "N3", "N5"));
我如何找到“N4”,我的意思是,我怎么发现丢失的整数是4?
到目前为止我尝试了什么
Integer missingID = arr.stream().map(p -> Integer.parseInt(p.substring(1))).sorted()
.reduce((p1, p2) -> (p2 - p1) > 1 ? p1 + 1 : 0).get();
这不起作用,因为reduce
不打算在这种情况下以我需要的方式工作,实际上,我不知道如何做到这一点。
如果没有丢失的号码,则下一个必须是"N6" - or just 6 -
(在此示例中)
必须使用java标准流的库,不使用第三方。
答案 0 :(得分:9)
这里要实现的算法基于this one:要找到整数序列中缺少的数字,诀窍是:
min
为max
。在这种情况下,我们可以首先映射到仅由数字本身组成的max*(max+1)/2 - (min-1)*min/2
,然后调用summaryStatistics()
来收集Stream
的统计信息。这将返回一个IntSummaryStatistics
,其中包含我们想要的所有值:min,max和sum:
IntStream
如果没有丢失号码,则会打印0。
答案 1 :(得分:4)
以下是我的免费StreamEx库中涉及pairMap
操作的解决方案。它打印排序输入的所有缺失元素:
ArrayList<String> arr = new ArrayList(Arrays.asList("N1", "N2", "N3", "N5"));
StreamEx.of(arr).map(n -> Integer.parseInt(n.substring(1)))
.pairMap((a, b) -> IntStream.range(a+1, b))
.flatMapToInt(Function.identity())
.forEach(System.out::println);
pairMap
操作允许您将每个相邻的流对映射到其他位置。在这里,我们将它们映射到跳过的数字的流,然后展平这些流。
没有第三方库可以使用相同的解决方案,但看起来更详细:
ArrayList<String> arr = new ArrayList(Arrays.asList("N1", "N2", "N3", "N5"));
IntStream.range(0, arr.size()-1)
.flatMap(idx -> IntStream.range(
Integer.parseInt(arr.get(idx).substring(1))+1,
Integer.parseInt(arr.get(idx+1).substring(1))))
.forEach(System.out::println);
答案 2 :(得分:3)
如果数组中只有 ONE 缺失数字,并且所有数字都是正数,则可以使用XOR算法,如this question and its answers中所述:
value_range
这种方法的优点是您不需要使用额外的数据结构,只需要几个List<String> list = Arrays.asList("N5", "N2", "N3", "N6");
int xorArray = list.stream()
.mapToInt(p -> Integer.parseInt(p.substring(1)))
.reduce(0, (p1, p2) -> p1 ^ p2);
int xorAll = IntStream.rangeClosed(2, 6)
.reduce(0, (p1, p2) -> p1 ^ p2);
System.out.println(xorArray ^ xorAll); // 4
s。
编辑:
此解决方案要求您事先知道数字的范围。虽然另一方面,它不需要对列表和流进行排序。
即使列表未排序,您仍然可以使用int
获得min
和max
(因此,范围),但这需要额外的迭代。
答案 3 :(得分:2)
您可以创建一个状态对象,用于将单个输入流转换为多个缺失条目流。然后可以对这些缺失的条目流进行平面映射以生成单个输出:
public class GapCheck {
private String last;
public GapCheck(String first) {
last = first;
}
public Stream<String> streamMissing(String next) {
final int n = Integer.parseInt(next.replaceAll("N", ""));
final int l = Integer.parseInt(last.replaceAll("N", ""));
last = next;
return IntStream.range(l + 1, n).mapToObj(Integer::toString);
}
}
用法:
final List<String> arr = new ArrayList(Arrays.asList("N1", "N3", "N5"));
arr.stream()
.flatMap(new GapCheck(arr.get(0))::streamMissing)
.forEach(System.out::println);
输出:
2
4
答案 4 :(得分:1)
这比您预期的更多,但可以通过collect
来完成。
public class Main {
public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<String>(Arrays.asList("N1", "N2", "N3", "N5", "N7", "N14"));
Stream<Integer> st = arr.stream().map(p -> Integer.parseInt(p.substring(1))).sorted();
Holder<Integer> holder = st.collect(() -> new Holder<Integer>(),
(h, i) -> {
Integer last = h.getProcessed().isEmpty() ? null : h.getProcessed().get(h.getProcessed().size() - 1);
if (last != null) {
while (i - last > 1) {
h.getMissing().add(++last);
}
}
h.getProcessed().add(i);
},
(h, h2) -> {});
holder.getMissing().forEach(System.out::println);
}
private static class Holder<T> {
private ArrayList<T> processed;
private ArrayList<T> missing;
public Holder() {
this.processed = new ArrayList<>();
this.missing = new ArrayList<>();
}
public ArrayList<T> getProcessed() {
return this.processed;
}
public ArrayList<T> getMissing() {
return this.missing;
}
}
}
打印
4
6
8
9
10
11
12
13
请注意,这种事情并不是特别适合Stream
。所有的流处理方法都倾向于将每个项目一次性传递给你,所以你需要同时处理所有缺失数字的运行,最后,你要编写大量的代码来避免只编写一个循环。
答案 5 :(得分:1)
这是使用纯流的一种解决方案,虽然效率不高。
public void test() {
List<String> arr = new ArrayList(
Arrays.asList("N1", "N2", "N3", "N5", "N7"));
List<Integer> list = IntStream
.range(1, arr.size())
.mapToObj(t -> new AbstractMap.SimpleEntry<Integer, Integer>(
extract(arr, t), extract(arr, t) - extract(arr, t - 1)))
.filter(t -> t.getValue() > 1)
.map(t -> t.getKey() - 1)
.collect(Collectors.toList());
System.out.println(list);
}
private int extract(List<String> arr, int t) {
return Integer.parseInt(arr.get(t).substring(1));
}
主要的性能块将是因为重复解析列表元素。但是,此解决方案将能够提供所有缺失的数字。