两个集合的LEFT OUTER JOIN

use*_*882 7 java collections

问题几乎在标题中.我正在寻找比通过集合完全搜索更有效的算法.

我有两个集合:

List<Map<TypeId, Object> > col1;
List<Entity> col2;
Run Code Online (Sandbox Code Playgroud)

哪里

public enum TypeId{
    PLAYER,
    PARTNER,
    PLATFORM,
    AMOUNT
}
Run Code Online (Sandbox Code Playgroud)

public class Entity{
    private int player_id;
    private int platform_id
    private BigDecimal amount;

    //GET, SET
}
Run Code Online (Sandbox Code Playgroud)

col1类型的集合List<Map<TypeId, Object> >仅包含PLAYER, PARTNER, PLATFORM TypeIds.

我需要写一个方法:

public List<Map<TypeId, Object> > merge(List<Map<TypeId, Object> > col1, List<Entity> col2){
    //Impl
}
Run Code Online (Sandbox Code Playgroud)

这是会产生List<Map<TypeId, Object> >各个入口entry地图包含其他键值(AMOUNT, AMOUNT's value) 哪里AMOUNT's value是价值amount的实例的领域eEntity,如果e.player_id = entry.get(PLAYER) && e.platform_id = entry.get(PLATFORM)null其他.

事实上,操作将是相同的

col1 LEFT OUTER JOIN 
col2 ON e.player_id = entry.get(PLAYER) && e.platform_id = entry.get(PLATFORM)
Run Code Online (Sandbox Code Playgroud)

样品:

col1:
[{PLATFORM: 1, PARTNER: 1, PLAYER: 1},
 {PLATFORM: 1, PARTNER: 3, PLAYER: 1},
 {PLATFORM: 2, PARTNER: 1, PLAYER: 2}
 {PLATFORM: 3, PARTNER: 4, PLAYER: 5}]

col2:
[Entity(platform_id = 1, player_id = 1, amount = 100),
Entity(platform_id = 2, player_id = 2, amount = 200),
Entity(platform_id = 3, player_id = 4, amount = 300)]

result:
[{PLATFORM: 1, PARTNER: 1, PLAYER: 1, AMOUNT: 100},
 {PLATFORM: 1, PARTNER: 3, PLAYER: 1, AMOUNT: 100},
 {PLATFORM: 2, PARTNER: 1, PLAYER: 2, AMOUNT: 200},
 {PLATFORM: 3, PARTNER: 4, PLAYER: 5, AMOUNT: null}]
Run Code Online (Sandbox Code Playgroud)

Tag*_*eev 3

就地更改更容易,修改col1列表而不是创建新的List. 这是 Java-8 解决方案:

public List<Map<TypeId, Object> > merge(List<Map<TypeId, Object> > col1, List<Entity> col2){
    col1.forEach(map -> map.put(TypeId.AMOUNT, 
        col2.stream()
            .filter(e -> e.player_id == (int)map.get(TypeId.PLAYER) && 
                         e.platform_id == (int)map.get(TypeId.PLATFORM))
            .findFirst().map(e -> e.amount).orElse(null)
        ));
    return col1;
}
Run Code Online (Sandbox Code Playgroud)

我认为col1在这种情况下改变就地是令人满意的。请注意,即使将结果存储到新列表中,如果修改现有地图也将毫无用处。因此,为了使结果完全独立于col1,您必须复制所有地图。

col1另请注意,对于它遍历的每个条目来说,它并不是非常有效col2,因此复杂度大约为col1.size()*col2.size()。在您的情况下,最好丢弃一个Entity类并创建一个仅存储 platformId 和 playerId 的新类(正确实现equalsand hashCode)并将其用作地图键:

public static class PlatformAndPlayer {
    private final int playerId, platformId;

    public PlatformAndPlayer(int playerId, int platformId) {
        this.playerId = playerId;
        this.platformId = platformId;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + platformId;
        result = prime * result + playerId;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        PlatformAndPlayer other = (PlatformAndPlayer) obj;
        if (platformId != other.platformId)
            return false;
        if (playerId != other.playerId)
            return false;
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样col2您将得到一个而不是列表Map

Map<PlatformAndPlayer, BigDecimal> col2 = new HashMap<>();
col2.put(new PlatformAndPlayer(1, 1), BigDecimal.valueOf(100));
col2.put(new PlatformAndPlayer(2, 2), BigDecimal.valueOf(200));
col2.put(new PlatformAndPlayer(3, 4), BigDecimal.valueOf(300));
Run Code Online (Sandbox Code Playgroud)

现在您的任务可以轻松有效地解决(即使使用 Java 5):

public static List<Map<TypeId, Object>> merge(
        List<Map<TypeId, Object>> col1,
        Map<PlatformAndPlayer, BigDecimal> col2) {
    for (Map<TypeId, Object> map : col1) {
        map.put(TypeId.AMOUNT, col2.get(new PlatformAndPlayer(
            (int) map.get(TypeId.PLAYER), (int) map.get(TypeId.PLATFORM))));
    }
    return col1;
}
Run Code Online (Sandbox Code Playgroud)