我有几个Integer对象的ArrayLists,存储在HashMap中。
我想得到每个列表中出现的所有数字(整数对象)的列表(ArrayList)。
到目前为止,我的想法是:
如果你能提出更快或更高效的东西,那么有趣的是我写的这个我想出了一个相当不错的解决方案。但我仍然会发布它以防它对其他人有用。
但当然如果你有更好的方法,请告诉我。
答案 0 :(得分:4)
我不确定我理解你的目标。但是如果你想找到List< Integer>的集合的交集。对象,然后您可以执行以下操作:
public static List<Integer> intersection(Collection<List<Integer>> lists){
if (lists.size()==0)
return Collections.emptyList();
Iterator<List<Integer>> it = lists.iterator();
HashSet<Integer> resSet = new HashSet<Integer>(it.next());
while (it.hasNext())
resSet.retainAll(new HashSet<Integer>(it.next()));
return new ArrayList<Integer>(resSet);
}
此代码以项目总数的线性时间运行。实际上这是平均线性时间,因为使用了HashSet。
另外,请注意,如果在循环中使用ArrayList.contains(),则可能会导致二次复杂度,因为此方法以线性时间运行,这与在常量时间运行的HashSet.contains()不同。
答案 1 :(得分:2)
您必须更改第1步: - 使用最短列表而不是hashSet(如果它不在最短列表中,则不在所有列表中......)
然后在其他列表上调用contains并在返回false时立即删除值(并跳过对该值的进一步测试)
最后,最短的列表将包含答案......
一些代码:
public class TestLists {
private static List<List<Integer>> listOfLists = new ArrayList<List<Integer>>();
private static List<Integer> filter(List<List<Integer>> listOfLists) {
// find the shortest list
List<Integer> shortestList = null;
for (List<Integer> list : listOfLists) {
if (shortestList == null || list.size() < shortestList.size()) {
shortestList = list;
}
}
// create result list from the shortest list
final List<Integer> result = new LinkedList<Integer>(shortestList);
// remove elements not present in all list from the result list
for (Integer valueToTest : shortestList) {
for (List<Integer> list : listOfLists) {
// no need to compare to itself
if (shortestList == list) {
continue;
}
// if one list doesn't contain value, remove from result and break loop
if (!list.contains(valueToTest)) {
result.remove(valueToTest);
break;
}
}
}
return result;
}
public static void main(String[] args) {
List<Integer> l1 = new ArrayList<Integer>(){{
add(100);
add(200);
}};
List<Integer> l2 = new ArrayList<Integer>(){{
add(100);
add(200);
add(300);
}};
List<Integer> l3 = new ArrayList<Integer>(){{
add(100);
add(200);
add(300);
}};
List<Integer> l4 = new ArrayList<Integer>(){{
add(100);
add(200);
add(300);
}};
List<Integer> l5 = new ArrayList<Integer>(){{
add(100);
add(200);
add(300);
}};
listOfLists.add(l1);
listOfLists.add(l2);
listOfLists.add(l3);
listOfLists.add(l4);
listOfLists.add(l5);
System.out.println(filter(listOfLists));
}
}
答案 2 :(得分:0)
Set
创建HashSet
(例如List
)。set.retainAll (list)
和list
都足够小,set
set.retainAll (new HashSet <Integer> (list))
我不能说在步骤2的第二个变体的哪个阈值变得更快之后,但我想大概可能是> 20
左右。如果你的名单都很小,你就不用费心了。
我记得,如果你不仅关心O(*)部分,而且关心因素,那么Apache Collections有更高效的整数结构。
答案 3 :(得分:0)
使用Google Collections Multiset使这个(表示方式)成为一个轻便小道(虽然我也喜欢Eyal's answer)。这可能不像其他人那样有效的时间/记忆方式,但是很明显发生了什么。
假设列表中没有重复项:
Multiset<Integer> counter = HashMultiset.create();
int totalLists = 0;
// for each of your ArrayLists
{
counter.addAll(list);
totalLists++;
}
List<Integer> inAll = Lists.newArrayList();
for (Integer candidate : counter.elementSet())
if (counter.count(candidate) == totalLists) inAll.add(candidate);`
如果列表可能包含重复元素,则可以先通过集合传递它们:
counter.addAll(list) => counter.addAll(Sets.newHashSet(list))
最后,如果您希望以后可能需要一些额外的数据(例如,某些特定值与切割的接近程度),这也是理想的选择。
另一种稍微修改Eyal的方法(基本上将通过集合过滤列表然后保留所有重叠元素的行为折叠在一起),并且比上面更轻量级:
public List<Integer> intersection(Iterable<List<Integer>> lists) {
Iterator<List<Integer>> listsIter = lists.iterator();
if (!listsIter.hasNext()) return Collections.emptyList();
Set<Integer> bag = new HashSet<Integer>(listsIter.next());
while (listsIter.hasNext() && !bag.isEmpty()) {
Iterator<Integer> itemIter = listsIter.next().iterator();
Set<Integer> holder = new HashSet<Integer>(); //perhaps also pre-size it to the bag size
Integer held;
while (itemIter.hasNext() && !bag.isEmpty())
if ( bag.remove(held = itemIter.next()) )
holder.add(held);
bag = holder;
}
return new ArrayList<Integer>(bag);
}