已经有一些回答了,但其中一些做出了某些无法从问题中推导出的假设,另一些建议某些可能被视为解决方法的更改,还有一些具有某些不应被忽视的缺点。
在此澄清其中一些:
最初的回答:
Changing the elements to a Set<Integer>
makes the assumption that each element may only appear once. Additionally, if the existing code already creates the int[]
arrays, and the downstream code needs the int[]
arrays, then using the resulting data structure would be clumsy:
int array[] = somewhere.getArray();
Set<Integer> set = convert(array);
data.add(set);
...
set = data.iterator().next();
array = convert(set);
somewhere.setArray(array);
Depending on the size of the arrays, this may have an impact on performance and generate some memory overhad.
Using a TreeSet<int[]>
looks like a simple and reasonable solution. The nicest thing is that it can directly store the int[]
arrays. But it has some drawbacks:
- It implies an ordering. It is no longer possible to use another
Set
implementation (like a LinkedHashSet
) that retains the insertion order
- It may be a bit tricky to implement the comparison in a way that is consistent with
equals
, and failing to do so will cause the set to no longer obey the general contract of the Set
interface
- A simple but correct implementation of the comparison will likely involve sorting the arrays. This means that the arrays might either have to be modified by their insertion into the set, which is certainly not acceptable, or one would have to create defensive copies of the arrays. Here, one has to keep in mind that the copy and the sorting will have to be done each and every time when a new array is added, and it will have to be done multiple times while going down the tree. Although the number of comparisons will only be O(log(n)) for a set with
n
elements, sorting will take O(m log(m)) each time for arrays of length m
, which may be prohibitively expensive.
- Similar arguments may be applied for the approaches that check whether an "equivalent" array already exists before inserting a new one. Additionally, these approaches are not based on a data structure, but have to be part of the code that uses the data structure.
出于这些原因,我会选择与
Mykhailo Moskura在他的回答中提到的方法基本相同:它基于一个简单地
包装给定的
int[]
数组,并相应地实现
equals
和
hashCode
。
(请注意,您也可以让该类实现
Comparable
,增加一些灵活性,以确定是否可以将其放入
TreeSet
中,如果您了解上述可能存在的困难和缺点...)
在下面的示例中,此类称为
UnorderedIntArray
。
从概念上讲,最好拥有一个
Set<int[]>
,而下面的解决方案必须使用
Set<UnorderedIntArray>
。但由于此类仅
包装现有数组,因此性能和内存开销几乎为
零,因此我仍然认为它比在
int[]
和其他某种数据类型之间进行转换更具优势。还请注意,下面示例中的
equals
方法不是非常高效,但它是确保遵守
equals
合同的简单方法。
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class UniqueArraysTest {
public static void main(String[] args) {
Set<UnorderedIntArray> result = new LinkedHashSet<>();
int[] x = { 1, 2, 3 };
int[] y = { 2, 1, 3 };
int[] z = { 2, 1, 3 };
int[] u = { 1, 1, 1, 2, 3 };
int[] v = { 1, 1, 1, 2, 3 };
int[] w = { 1, 1, 1, 1, 3 };
result.add(new UnorderedIntArray(x));
result.add(new UnorderedIntArray(y));
result.add(new UnorderedIntArray(z));
result.add(new UnorderedIntArray(u));
result.add(new UnorderedIntArray(v));
result.add(new UnorderedIntArray(w));
for (UnorderedIntArray a : result) {
int[] array = a.getArray();
System.out.println(Arrays.toString(array));
}
}
static class UnorderedIntArray {
private final int array[];
UnorderedIntArray(int array[]) {
this.array = array;
}
int[] getArray() {
return array;
}
@Override
public int hashCode() {
return IntStream.of(array).sum();
}
@Override
public boolean equals(Object object) {
if (object == this) {
return true;
}
if (object == null) {
return false;
}
if (!(object instanceof UnorderedIntArray)) {
return false;
}
UnorderedIntArray that = (UnorderedIntArray)object;
if (this.array.length != that.array.length) {
return false;
}
Map<Integer, Long> thisFrequencies = computeFrequencies(this.array);
Map<Integer, Long> thatFrequencies = computeFrequencies(that.array);
return thisFrequencies.equals(thatFrequencies);
}
private Map<Integer, Long> computeFrequencies(int array[]) {
return Arrays.stream(array).boxed().collect(
Collectors.groupingBy(Function.identity(), Collectors.counting()));
}
@Override
public String toString() {
return Arrays.toString(array);
}
}
}
最初的回答
对于输入的内容
int[] x = { 1, 2, 3 };
int[] y = { 2, 1, 3 };
int[] z = { 2, 1, 3 };
int[] u = { 1, 1, 1, 2, 3 };
int[] v = { 1, 1, 1, 2, 3 };
int[] w = { 1, 1, 1, 1, 3 };
最初的回答是输出符合预期。
[1, 2, 3]
[1, 1, 1, 2, 3]
[1, 1, 1, 1, 3]
Set<Integer>
而不是int[]
将提供您想要的相等关系。 - John BollingerSet<Integer>
中元素的hashCode
只是单个代码的相加,我从AbstractSet
类中的实现可以看出来:public int hashCode() {int h = 0;Iterator<E> i = iterator();while (i.hasNext()) {E obj = i.next();if (obj != null)h += obj.hashCode();}return h;}
在这种情况下,Set<Set<Integer>>
不会起到作用,它会认为{0,1,2}
和{1,1,1}
相等。 - SomeDudeArrayList
,尽管问题实际上是关于Set
的。是否有任何反对将标题更改为类似于“维护一个集合...”或“维护一个collection...”的内容? - Marco13