两个不同Java对象的“左连接”
我有一个Object1( List
)列表和一个Object2 List
( List
)
- 对象1具有多个属性,包括
id
- 对象2有多个
object1id
,包括object1id
我有一些SQL背景,我想要做的是执行“左连接”
object1.id = object2.object1id
这将导致List
表示左连接。 我可以用Java硬编码算法(for … for …),但我确信这至少在n * m的复杂度下效率不高。
你有更好的解决方案吗? (如果可能,请使用代码,谢谢!)
您正在尝试做一些Java并不是真正意义上的事情。
如果能够这样做,最好向Object1
添加一个属性 ,它将是包含与this
相关的对象的Object2
列表。
如果你不能,我们仍然可以选择天真地做,否则你可以尝试这样的事情:
HashSet hs = new HashSet (list2.size()); for(Object2 o : list2) { hs.add(o.object1id); } //hs contains all the ids of list2 List result = new ArrayList (); //Or another class implementing List for(Object1 o : list1) { if(hs.contains(o.id)) result.add(o); }
因为你必须将所有id存储在HashSet中,所以并不漂亮,但由于在HashSet中添加和访问元素是O(1)(理论上),算法是O(n + m)
如果您的Object3
类是使用Object1
和Object2
构造的,请使用HasMap
而不是HashSet
,其中键是id,值是object2。 代码中的最后一个for
循环将变为:
Object2 o2 = hs.get(o.id); if(o2 != null) result.add(new Object3(o, o2);
继ÓscarLópez发表评论:
如果您的objectid1不是唯一的,您必须按如下方式调整代码:
HashMap> hm = new HashMap>(); for(Object2 o : list2) { List l = hm.get(o.objectid1); if(l != null) { l.add(o); } else { List l = new ArrayList (); l.add(o); hm.put(o.objectid1, l); } //hm is map, where each entry contains the list of Object2 associated with objectid1 List result = new ArrayList (); for(Object1 o : list1) { List l = hm.get(o.id); //l contains all Object2 with object1id = o.id for(Object2 o2 : l) result.add(new Object3(o, o2)); }
仍然在O(n + m),但具有更大的常数……
在List上创建索引。 扫描列表并填写索引:
HashMap index=HashMap(); for (Object2 obj2: list2) { index.put(obj2.object1id, obj2); }
然后,扫描列表并进行连接:
for (Object1 obj1: list1) { Object2 obj2=index.get(obj1.id); // may be null Object3 obj3=new Object3(obj1, obj2); }
如果您使用的是Java 8,则可以利用流 。 它可能看起来像这样(假设id
是要查找的Object1的id):
List newList = obj2List.stream().filter(x -> x.object1id == id).map(x -> obj2To3(x)).collect(Collectors.toList());
提供的案例非常模糊,因此很难给出更详细的答案。
一个好的解决方案可能是将Object2的List转换为Map。 然后遍历Object1 List并从Map获取Object2,最终创建Join并在Object3 List中添加结果。
我认为O(n*m)
解决方案是不可避免的, 除非创建更复杂的数据结构基础结构 – 使用索引,哈希等实现数据库中的高效连接。还要记住正确的实现应该考虑这样的情况: list2
中的多个对象具有相同的object1id
– 我的代码在这种情况下工作,但是只是将obj2.object1id
添加到Set
或作为Map
键的所有解决方案都将失败。
但是实现的复杂性值得吗? 如果输入列表很小, O(n*m)
解决方案就可以正常工作。 这是我的建议,使用好的旧嵌套循环:
List list3 = new ArrayList<>(); for (Object1 obj1 : list1) { boolean found = false; for (Object2 obj2 : list2) { if (obj1.id.equals(obj2.object1id)) { list3.add(new Object3(obj1, obj2)); found = true; } } if (!found) list3.add(new Object3(obj1, null)); }
为了使上述工作,我使用的输出对象如下所示:
public class Object3 { private Object1 obj1; private Object2 obj2; public Object3(Object1 obj1, Object2 obj2) { this.obj1 = obj1; this.obj2 = obj2; } }
如果它们实现了一些通用接口(这会使事情变得更容易,尤其是使用转换),那么这是相对简单的。
它仍然是O(nm),因为您必须遍历列表的两个长度才能找到要添加的元素。
public interface JoinInterface { int getId(); int getObject1Id(); // likely baggage here } public static List extends JoinableEntity> leftJoin(List extends JoinableEntity> left, List extends JoinableEntity> right) { List result = new ArrayList<>(); result.addAll(left); for(JoinableEntity aLeft : left) { for(JoinableEntity aRight : right) { if(aLeft.getId() == aRight.getObject1Id()) { result.add(aRight); break; } } } return result; }