You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中基于双唯一属性外连接两个对象列表生成新列表

Alright, let's solve this outer join problem where we need to combine two lists (Users and Academics) using both id and name as the unique matching keys. I'll walk through a full outer join solution (includes entries from either list even if there's no match in the other) since that's the most common use case, and also note how to adjust for left/right joins if needed.


Step 1: Define a Combined Result Class

First, we need a class to hold the joined data — this lets us store attributes from both Users and Academics, even when one side has no matching entry.

public class UserAcademic {
    private Long id;
    private String name;
    private String gender; // From Users
    private String grade; // From Academics
    private String professional; // From Academics

    // Constructor, getters, setters, and toString() for usability
    public UserAcademic(Long id, String name, String gender, String grade, String professional) {
        this.id = id;
        this.name = name;
        this.gender = gender;
        this.grade = grade;
        this.professional = professional;
    }

    // Add getters and setters here (omitted for brevity)
    @Override
    public String toString() {
        return "UserAcademic{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", grade='" + grade + '\'' +
                ", professional='" + professional + '\'' +
                '}';
    }
}

Step 2: Create a Composite Key for Matching

Since we're matching on two fields (id and name), we need a composite key to use as a lookup in a map. For Java 16+, a Record is perfect (it automatically handles equals() and hashCode()):

// Composite key using Java 16+ Record
public record UserKey(Long id, String name) {}

If you're on a pre-Java 16 version, use a custom class with properly overridden equals() and hashCode():

// Composite key for pre-Java 16
import java.util.Objects;

public class UserKey {
    private Long id;
    private String name;

    public UserKey(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        UserKey userKey = (UserKey) o;
        return Objects.equals(id, userKey.id) && Objects.equals(name, userKey.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

Step 3: Implement the Full Outer Join Logic

We'll use a HashMap to efficiently map composite keys to our combined UserAcademic objects. This gives us O(n + m) time complexity (way better than nested loops):

import java.util.*;
import java.util.stream.Collectors;

public class OuterJoinExecutor {
    public static void main(String[] args) {
        // Your pre-initialized userList
        List<Users> userList = Arrays.asList(
                new Users(1L, "Alice", "Female"),
                new Users(2L, "Bob", "Male"),
                new Users(3L, "Charlie", "Male")
        );

        // Sample academicsList (replace with your initialized list)
        List<Academics> academicsList = Arrays.asList(
                new Academics(1L, "Alice", "A", "Computer Science"),
                new Academics(2L, "Bob", "B", "Mathematics"),
                new Academics(4L, "Diana", "A", "Physics")
        );

        // Run the outer join
        List<UserAcademic> joinedResult = performFullOuterJoin(userList, academicsList);

        // Print results to verify
        joinedResult.forEach(System.out::println);
    }

    private static List<UserAcademic> performFullOuterJoin(List<Users> userList, List<Academics> academicsList) {
        Map<UserKey, UserAcademic> joinMap = new HashMap<>();

        // First add all Users entries (Academics fields will be null initially)
        for (Users user : userList) {
            UserKey key = new UserKey(user.getId(), user.getName());
            joinMap.put(key, new UserAcademic(user.getId(), user.getName(), user.getGender(), null, null));
        }

        // Now process Academics: update existing entries or add new ones
        for (Academics academic : academicsList) {
            UserKey key = new UserKey(academic.getId(), academic.getName());
            if (joinMap.containsKey(key)) {
                // Update the existing entry with Academics data
                UserAcademic existingEntry = joinMap.get(key);
                existingEntry.setGrade(academic.getGrade());
                existingEntry.setProfessional(academic.getProfessional());
            } else {
                // Add a new entry (User fields will be null)
                joinMap.put(key, new UserAcademic(academic.getId(), academic.getName(), null, academic.getGrade(), academic.getProfessional()));
            }
        }

        // Convert map values to a list for the final result
        return new ArrayList<>(joinMap.values());
    }
}

// Your existing Users class
class Users {
    private Long id;
    private String name;
    private String gender;

    public Users(Long id, String name, String gender) {
        this.id = id;
        this.name = name;
        this.gender = gender;
    }

    // Getters (required for the join logic)
    public Long getId() { return id; }
    public String getName() { return name; }
    public String getGender() { return gender; }
}

// Your Academics class
class Academics {
    private Long id;
    private String name;
    private String grade;
    private String professional;

    public Academics(Long id, String name, String grade, String professional) {
        this.id = id;
        this.name = name;
        this.grade = grade;
        this.professional = professional;
    }

    // Getters (required for the join logic)
    public Long getId() { return id; }
    public String getName() { return name; }
    public String getGrade() { return grade; }
    public String getProfessional() { return professional; }
}

Key Notes & Variations

  • Full Outer Join: The above code includes every entry from both lists. Missing fields will be null (e.g., Diana will have gender: null, Charlie will have grade: null and professional: null).
  • Left Outer Join: If you only want to keep entries from userList (even without a matching Academics entry), remove the else block where we add new entries from academicsList.
  • Right Outer Join: To keep only entries from academicsList, reverse the order: first add all Academics entries to the map, then update with matching Users entries.

内容的提问来源于stack exchange,提问作者Jayendran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:57:45