Java 如何连接两组对象
我有两组对象,每一组的对象中都有一个公共的“键”。我想要一个结果集/列表,它将具有多个属性的连接。 比如说, 设置一个:Java 如何连接两组对象,java,Java,我有两组对象,每一组的对象中都有一个公共的“键”。我想要一个结果集/列表,它将具有多个属性的连接。 比如说, 设置一个:[{id:1,姓名:john,年龄:22},{..}] 集合B:[{id:1,电话:1234,类型:mobile},{..}] 设置结果:[{id:1,姓名:john,年龄:22,电话:1234,类型:mobile},{..}] 在不使用循环或不将集合转换为Hashmap的情况下可以实现这一点吗? 谢谢。我假设您的集合只是两个Java集合的可视化表示: Set<User&
[{id:1,姓名:john,年龄:22},{..}]
集合B:[{id:1,电话:1234,类型:mobile},{..}]
设置结果:[{id:1,姓名:john,年龄:22,电话:1234,类型:mobile},{..}]
在不使用循环或不将集合转换为Hashmap的情况下可以实现这一点吗?
谢谢。我假设您的
集合
只是两个Java集合
的可视化表示:
Set<User>
Set<Phone>
现在只需执行以下代码:
for (User u : users) {
usersWithPhone.put(u.id, new UserWithPhone(u));
}
for (Phone p : phones) {
usersWithPhone.get(p.id).setPhoneDetails(p);
}
其中
usersWithPhone
是一个Map
。好。。。我知道这不是你想要的。。。我的意思是有循环,地图。。。但这是我们在Java中如何做到的…因此,看起来您对循环的关注更多的是为了避免暴力方法,而不是实际使用循环
这里有一个想法,但它要求您的集合必须预先排序,否则就无法合并集合而不进行多次迭代
假设你有两套:用户和电话
users = [{1, "john"}, {2, "ken"}]
phones = [{2, "555-1234"}, {4, "234-23424"}]
您可以做的是在每个集合的当前id不同时“迭代”它们。这里重要的一点是只迭代id低于另一个id的集合,因此如果用户中的id很小,则在用户集合中行走,如果电话集合中的id较低,则在电话集合中行走。通过这种方式,您不会对每个集合进行多次迭代,但最多迭代N次,其中N是用户集合的长度
因此,在本例中,您从id:1和2开始
users[0].id == 1
phones[0].id == 2
因为它们是不同的,所以在用户索引上移动
users[1].id == 2
phones[0].id == 2
现在他们是一样的。。。在这种情况下,您将合并对象并创建一个新的联系人
现在,您可以在两个索引上移动并重复,除非您在其中一个集合的末尾,在这种情况下,您就完成了
基本上类似于这个伪代码
// while the id's are different
while( users[usersIndex].id != phones[phoneIndex].id ) {
if (user.id < phone.id) { usersIndex++ );
if (phone.id < user.id) { phoneIndex++ );
}
// At this point either they are the same ... OR one we are at the end of one of the collections
if (user.id == phone.id ) { result.add( new Contact(user, phone) );}
else { we are done }
... repeat.
在这一点上,你可能会想“是的,这一切都很好,但我怎么知道它是有效的?”
下面是代码,合并两个集合而不迭代超过N倍的集合,并用结果创建一个新集合
在本例中,我使用Lombok注释。。。因为它很棒,它基本上为您创建了getter/setter、toString()、equals和hashCode方法,所以您不必编写它们
package merge;
import lombok.Builder;
import lombok.Data;
import java.util.Set;
import java.util.TreeSet;
public class Main {
public static void main(String[] args) {
Merge m = new Merge();
System.out.println("Result= " +
m.merge(
buildUsers(),
buildPhones(),
new TreeSet<>()
)
);
}
private static Set<User> buildUsers() {
Set<User> users = new TreeSet<>();
users.add(new User(1, "j"));
users.add(new User(3, "k"));
return users;
}
private static Set<Phone> buildPhones() {
Set<Phone> phones = new TreeSet<>();
phones.add(new Phone(1, "123"));
phones.add(new Phone(2, "345"));
phones.add(new Phone(3, "678"));
return phones;
/// KEEP SCROLLING
}
}
class Merge {
public Set<Contact> merge(Set<User> users, Set<Phone> phones, Set<Contact> contacts) {
if (users.isEmpty() || phones.isEmpty()) {
return contacts;
}
User user = users.iterator().next();
Phone phone = phones.iterator().next();
if (user.getId() == phone.getId()) {
addContact(contacts, user, phone);
users.remove(user);
phones.remove(phone);
} else if (user.getId() < phone.getId()) {
users.remove(user);
} else {
phones.remove(phone);
}
return merge(users, phones, contacts);
}
private boolean addContact(Set<Contact> contacts, User user, Phone phone) {
return contacts.add(Contact.builder()
.id(user.getId())
.name(user.getName())
.phone(phone.getPhone())
.build());
}
}
@Data
class User implements Comparable<User> {
private final int id;
private final String name;
@Override
public int compareTo(User o) {
return Integer.compare(this.id, o.id);
}
}
@Data
class Phone implements Comparable<Phone> {
final int id;
final String phone;
@Override
public int compareTo(Phone o) {
return Integer.compare(this.id, o.id);
}
}
@Data
@Builder
class Contact implements Comparable<Contact> {
int id;
String name;
String phone;
@Override
public int compareTo(Contact o) {
return Integer.compare(this.id, o.id);
}
}
“不使用循环就可以实现这一点”。一个响亮的否定。你对循环有什么担心?有趣的事实是,如果你是一个HashSet(或它的孩子),它与HashMap完全相同。(只需查看源代码)。那么你为什么不想换成HashMap呢?(两次投票?真的吗?)这些集合的内容是什么?我的意思是,这些物体有哪种类型?(看起来它们类似于
Map
)。当您不知道对象的类型,并且根本不知道如何“合并”它们时,就根本不会出现关于循环的问题。(@boristesspider…或关于递归的问题;-))生成的equals
和hashCode
与equals
不太一致;这并不理想,但也不太重要,除非OP希望Set
删除具有不同名称的重复用户ID。我会避免变异集合
,而采用更具功能性的方法(如您建议的ConsList
pseudocode)-使用TreeSet.tailSet(item,false)
。与比较一致你的意思是?你是对的,我认为应该有一种方法使它保持一致,可能不会使字符串
属性成为最终属性?关于改变集合,我同意你的观点,我最初尝试传递迭代器,但当我在验证其空性时遇到问题时,我记得这是一个概念证明,而不是一个准备生产的代码,然后从集合中删除项,以使我的算法能够正常实现。我想看看TreeSet.tailSet
是的,我的意思是“您的比较将与生成的equals
和hashCode
不太一致”。我将添加@EqualsAndHashcode(of=“id”)
以将equals
限制为仅包含id
属性,但这实际上取决于OP对这些对象的行为的确切预期。您可以使用NavigableSet.first()
和NavigableSet.tailSet
,生成的代码应该与您的消费者几乎相同。非常感谢。它帮助我理解了如何解决这个问题。你说的“对循环的暴力关注”是对的
merge( users, phones, results ) {
// base case, if one of them is empty
// we're done
if users.isEmpty() OR phones.isEmpty()
return results
// take the first element on each set and compare them
user = users.head
phone = phones.head
// if they're the same create a new contact and remove them
if user.id == phone.id
results.add( new Contact(user, phone))
users.remove(user)
phones.remove(phone)
// iterate on the users set
if user.id < phone.id
// we won't find a matching phone
users.remove(user)
// iterate on the phone set
if phone.id < user.id
// we won't find a matching user
phones.remove(phone)
// call again with the modified sets
return merge(users, phones, results)
}
package merge;
import lombok.Builder;
import lombok.Data;
import java.util.Set;
import java.util.TreeSet;
public class Main {
public static void main(String[] args) {
Merge m = new Merge();
System.out.println("Result= " +
m.merge(
buildUsers(),
buildPhones(),
new TreeSet<>()
)
);
}
private static Set<User> buildUsers() {
Set<User> users = new TreeSet<>();
users.add(new User(1, "j"));
users.add(new User(3, "k"));
return users;
}
private static Set<Phone> buildPhones() {
Set<Phone> phones = new TreeSet<>();
phones.add(new Phone(1, "123"));
phones.add(new Phone(2, "345"));
phones.add(new Phone(3, "678"));
return phones;
/// KEEP SCROLLING
}
}
class Merge {
public Set<Contact> merge(Set<User> users, Set<Phone> phones, Set<Contact> contacts) {
if (users.isEmpty() || phones.isEmpty()) {
return contacts;
}
User user = users.iterator().next();
Phone phone = phones.iterator().next();
if (user.getId() == phone.getId()) {
addContact(contacts, user, phone);
users.remove(user);
phones.remove(phone);
} else if (user.getId() < phone.getId()) {
users.remove(user);
} else {
phones.remove(phone);
}
return merge(users, phones, contacts);
}
private boolean addContact(Set<Contact> contacts, User user, Phone phone) {
return contacts.add(Contact.builder()
.id(user.getId())
.name(user.getName())
.phone(phone.getPhone())
.build());
}
}
@Data
class User implements Comparable<User> {
private final int id;
private final String name;
@Override
public int compareTo(User o) {
return Integer.compare(this.id, o.id);
}
}
@Data
class Phone implements Comparable<Phone> {
final int id;
final String phone;
@Override
public int compareTo(Phone o) {
return Integer.compare(this.id, o.id);
}
}
@Data
@Builder
class Contact implements Comparable<Contact> {
int id;
String name;
String phone;
@Override
public int compareTo(Contact o) {
return Integer.compare(this.id, o.id);
}
}
javac -cp lib/lombok.jar src/merge/Main.java -d out/
java -cp lib/lombok.jar:out merge.Main
Result= [Contact(id=1, name=j, phone=123), Contact(id=3, name=k, phone=678)]