使用 Streams 在两个列表 List<int[]> 中查找重复项
Find duplicates in two lists List<int[]> using Streams
使用流在两个列表 List 中查找重复项
我有一个 list 整数数组。我需要使用 2
int 数组列表找到这些数组的副本。
我确实尝试实现它,但我得到的是一个空数组。
keys = [[1,1,0], [0,0,1], [1,2,1], [1,3,1], [1,3,2]];
phaseKey = [[1,3,2], [1,2,1], [0,0,2], [1,2,3], [1,0,3]];
desired result: [[[1,3,2]], [1,2,1]];
我的代码:
Stream.concat(Stream.of(keys.stream().flatMapToInt(Arrays::stream)),
Stream.of(phaseKey.stream().flatMapToInt(Arrays::stream)))
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()))
.entrySet()
.stream()
.filter(m -> m.getValue() > 1)
.map(Map.Entry::getKey)
.toArray();
鉴于您的两个列表:
List<int[]> keys = // ....
List<int[]> phaseKey = //...
您只需要过滤以在两个列表中找到共同的数组:
List<int[]> duplicates = keys.stream()
.filter(k -> phaseKey.stream().anyMatch(p -> Arrays.equals(p,k)))
.collect(Collectors.toList());
正如@Pshemo 已经在评论中指出的那样,数组没有正确实现 equals()
和 hashCode()
方法。如果对于要用作地图中的键的每个对象,这都是至关重要的。
Java中的数组很特别。它们不是通过实例化 class 创建的,但它们是对象,并且它们从 java.lang.Object
class 派生所有方法。因此,可通过数组访问的 equals()
和 hashCode()
可用于建立唯一性,即这两个数组是否由内存中的同一对象表示。
为了确定两个数组的内容是否相同,您需要使用 Arrays
实用程序 class 提供的静态方法 equals()
和 hashCode()
。为了能够使用此功能,您可以创建一个 class 来包裹数组,根据数组内容实现 equals/hashCode
合约。
我选择将此包装器实现为 Java 16 record
以使其更精简,但您可以将其更改为常规 class.
public record ArrayWrapper(int[] arr) {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ArrayWrapper other = (ArrayWrapper) o;
return Arrays.equals(arr, other.arr);
}
@Override
public int hashCode() {
return Arrays.hashCode(arr);
}
}
现在任何被该记录包裹的数组都可以用于 hash-based 数据结构。
因为你的例子有歧义,下面我列出了两种不同的实现方式。
选择两个列表中都存在的数组
为了检查 phaseKey
列表中包含的特定数组是否也存在于 keys
列表中,我们创建一个 HashSet
的 [=27] =] 对象,然后对该集合执行检查。这将允许在 线性时间 中解决此任务,通过每个列表仅执行 单次传递 。
public static void main(String[] args) {
List<int[]> keys = List.of(new int[]{1, 1, 0}, new int[]{0, 0, 1}, new int[]{1, 2, 1},
new int[]{1, 3, 1}, new int[]{1, 3, 2});
List<int[]> phaseKey = List.of(new int[]{1, 3, 2}, new int[]{1, 2, 1}, new int[]{0, 0, 2},
new int[]{1, 2, 3}, new int[]{1, 0, 3});
Set<ArrayWrapper> wrappedKeys = keys.stream().map(ArrayWrapper::new).collect(Collectors.toSet());
List<int[]> result = phaseKey.stream()
.map(ArrayWrapper::new)
.filter(wrappedKeys::contains)
.distinct() // to ensure that each array will be present in the array only once
.map(ArrayWrapper::arr)
.collect(Collectors.toList()); // toList() with Java 16+
result.forEach(arr -> System.out.println(Arrays.toString(arr)));
}
正在获取出现次数超过一次的数组
要查明特定数组是否在给定列表之一(或两个列表)中出现多次,我们可以通过将任何键的初始值分配为 [=29] 来创建中间映射 Map<ArrayWrapper,Boolean>
=](不是重复项),并从旨在解决重复项的 mergeFunction
返回 true
。
public static void main(String[] args) {
List<int[]> keys = List.of(new int[]{1, 1, 0}, new int[]{0, 0, 1}, new int[]{1, 2, 1},
new int[]{1, 3, 1}, new int[]{1, 3, 2});
List<int[]> phaseKey = List.of(new int[]{1, 3, 2}, new int[]{1, 2, 1}, new int[]{0, 0, 2},
new int[]{1, 2, 3}, new int[]{1, 0, 3});
List<int[]> result = Stream.of(keys, phaseKey)
.flatMap(List::stream)
.map(ArrayWrapper::new)
.collect(Collectors.toMap( // creates an intermediate map Map<ArrayWrapper, Boolean>
Function.identity(),
next -> false, // first occurrence
(left, right) -> true)) // all subsequent occurrences
.entrySet().stream()
.filter(Map.Entry::getValue)
.map(Map.Entry::getKey)
.map(ArrayWrapper::arr)
.collect(Collectors.toList()); // toList() with Java 16+
result.forEach(arr -> System.out.println(Arrays.toString(arr)));
}
输出
[1, 3, 2]
[1, 2, 1]
使用流在两个列表 List
我有一个 list 整数数组。我需要使用 2
int 数组列表找到这些数组的副本。
我确实尝试实现它,但我得到的是一个空数组。
keys = [[1,1,0], [0,0,1], [1,2,1], [1,3,1], [1,3,2]];
phaseKey = [[1,3,2], [1,2,1], [0,0,2], [1,2,3], [1,0,3]];
desired result: [[[1,3,2]], [1,2,1]];
我的代码:
Stream.concat(Stream.of(keys.stream().flatMapToInt(Arrays::stream)),
Stream.of(phaseKey.stream().flatMapToInt(Arrays::stream)))
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()))
.entrySet()
.stream()
.filter(m -> m.getValue() > 1)
.map(Map.Entry::getKey)
.toArray();
鉴于您的两个列表:
List<int[]> keys = // ....
List<int[]> phaseKey = //...
您只需要过滤以在两个列表中找到共同的数组:
List<int[]> duplicates = keys.stream()
.filter(k -> phaseKey.stream().anyMatch(p -> Arrays.equals(p,k)))
.collect(Collectors.toList());
正如@Pshemo 已经在评论中指出的那样,数组没有正确实现 equals()
和 hashCode()
方法。如果对于要用作地图中的键的每个对象,这都是至关重要的。
Java中的数组很特别。它们不是通过实例化 class 创建的,但它们是对象,并且它们从 java.lang.Object
class 派生所有方法。因此,可通过数组访问的 equals()
和 hashCode()
可用于建立唯一性,即这两个数组是否由内存中的同一对象表示。
为了确定两个数组的内容是否相同,您需要使用 Arrays
实用程序 class 提供的静态方法 equals()
和 hashCode()
。为了能够使用此功能,您可以创建一个 class 来包裹数组,根据数组内容实现 equals/hashCode
合约。
我选择将此包装器实现为 Java 16 record
以使其更精简,但您可以将其更改为常规 class.
public record ArrayWrapper(int[] arr) {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ArrayWrapper other = (ArrayWrapper) o;
return Arrays.equals(arr, other.arr);
}
@Override
public int hashCode() {
return Arrays.hashCode(arr);
}
}
现在任何被该记录包裹的数组都可以用于 hash-based 数据结构。
因为你的例子有歧义,下面我列出了两种不同的实现方式。
选择两个列表中都存在的数组
为了检查 phaseKey
列表中包含的特定数组是否也存在于 keys
列表中,我们创建一个 HashSet
的 [=27] =] 对象,然后对该集合执行检查。这将允许在 线性时间 中解决此任务,通过每个列表仅执行 单次传递 。
public static void main(String[] args) {
List<int[]> keys = List.of(new int[]{1, 1, 0}, new int[]{0, 0, 1}, new int[]{1, 2, 1},
new int[]{1, 3, 1}, new int[]{1, 3, 2});
List<int[]> phaseKey = List.of(new int[]{1, 3, 2}, new int[]{1, 2, 1}, new int[]{0, 0, 2},
new int[]{1, 2, 3}, new int[]{1, 0, 3});
Set<ArrayWrapper> wrappedKeys = keys.stream().map(ArrayWrapper::new).collect(Collectors.toSet());
List<int[]> result = phaseKey.stream()
.map(ArrayWrapper::new)
.filter(wrappedKeys::contains)
.distinct() // to ensure that each array will be present in the array only once
.map(ArrayWrapper::arr)
.collect(Collectors.toList()); // toList() with Java 16+
result.forEach(arr -> System.out.println(Arrays.toString(arr)));
}
正在获取出现次数超过一次的数组
要查明特定数组是否在给定列表之一(或两个列表)中出现多次,我们可以通过将任何键的初始值分配为 [=29] 来创建中间映射 Map<ArrayWrapper,Boolean>
=](不是重复项),并从旨在解决重复项的 mergeFunction
返回 true
。
public static void main(String[] args) {
List<int[]> keys = List.of(new int[]{1, 1, 0}, new int[]{0, 0, 1}, new int[]{1, 2, 1},
new int[]{1, 3, 1}, new int[]{1, 3, 2});
List<int[]> phaseKey = List.of(new int[]{1, 3, 2}, new int[]{1, 2, 1}, new int[]{0, 0, 2},
new int[]{1, 2, 3}, new int[]{1, 0, 3});
List<int[]> result = Stream.of(keys, phaseKey)
.flatMap(List::stream)
.map(ArrayWrapper::new)
.collect(Collectors.toMap( // creates an intermediate map Map<ArrayWrapper, Boolean>
Function.identity(),
next -> false, // first occurrence
(left, right) -> true)) // all subsequent occurrences
.entrySet().stream()
.filter(Map.Entry::getValue)
.map(Map.Entry::getKey)
.map(ArrayWrapper::arr)
.collect(Collectors.toList()); // toList() with Java 16+
result.forEach(arr -> System.out.println(Arrays.toString(arr)));
}
输出
[1, 3, 2]
[1, 2, 1]