如何加入两组对象

时间:2016-10-26 19:33:01

标签: java

我有两组对象,每组对象中都有一个共同的“键”。我想要一个结果集/列表,它将具有属性的连接。 例如,

设置A:[{id:1,name:john,age:22},{..}]

设置B:[{id:1,phone:1234,type:mobile},{..}]

设置结果:[{id:1,name:john,age:22,phone:1234,type:mobile},{..}]

这可以在不使用循环或不将集合转换为Hashmap的情况下实现吗? 感谢。

2 个答案:

答案 0 :(得分:1)

所以,看起来你对循环的关注更多的是避免使用暴力方法而不是实际使用循环。

这是一个想法,但它要求您的集合必须预先订购,否则无法合并集合而不会多次迭代它们。

假设您有两套:用户和电话

users =  [{1, "john"}, {2, "ken"}]
phones = [{2, "555-1234"}, {4, "234-23424"}]

你可以做的是"迭代"每个集合,而他们当前的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

现在它们是相同的...在这种情况下,您合并对象并创建一个新的Contact

现在你移动两个索引并重复,除非你在任何一个集合的末尾,在这种情况下你就完成了。

所以,基本上就像这个伪代码

// 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. 

现在,我试图这样做,但它变得棘手,首先是因为实例java.util.Set没有使用索引,使用迭代器需要几个额外的验证哦,是的,那里&# 39;你的要求是不使用循环。事实证明,使用递归可以非常清晰地解决问题。

使用递归的算法将是这样的:

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)
}

此时你可能会想到&#34;是的,这一切都很好,但我怎么知道它有效&#34;

这里是代码,合并两个集合而不会迭代N次以上的集合并创建一个带有结果的新集合。

在这个例子中,我使用Lombok @Data注释...只是因为它很棒,它基本上为你创建了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);
    }
}

运行

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)]

答案 1 :(得分:0)

我假设你的Set只是两个Java Set的直观表示:

Set<User>
Set<Phone>

可以在没有循环的情况下对Set执行操作吗?好吧,可能你可以用流来做某种方式,但我建议如下:

public class UserWithPhone {
    Long id;
    String name;
    Long age;
    String number;
    PhoneType phoneType;

    UserWithPhone(){};
    UserWithPhone(User u, Phone p) {
        if (!u.id.equals(p.id))
            throw new IllegalArgumentException();
        this.id = u.id;
        this.name = u.name;
        this.age = u.age;
        this.number = p.number;
        this.phoneType = p.type;
    }

    UserWithPhone(User u) {
        this.id = u.id;
        this.name = u.name;
        this.age = u.age;
    }
    setPhoneDetails(Phone p) {
        if (!this.id.equals(p.id))
            throw new IllegalArgumentException();
        this.number = p.number;
        this.phoneType = p.type;
    }
}

现在只需执行以下代码:

for (User u : users) {
    usersWithPhone.put(u.id, new UserWithPhone(u));
}
for (Phone p : phones) {
    usersWithPhone.get(p.id).setPhoneDetails(p);
}

usersWithPhoneMap的位置。嗯......我知道它不是你想要的......我的意思是有循环,地图......但它是我们在Java中如何做到这一点...