首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java 8多键分组

Java 8多键分组
EN

Stack Overflow用户
提问于 2020-10-21 15:42:52
回答 2查看 945关注 0票数 2

我正在寻找一些帮助,从一个列表多个键的对象分组列表。

基本上,我有一个用户列表,其中包含了他们的订单列表,我希望能够使用UserName和Address作为密钥对他们进行分组。

示例数据:

代码语言:javascript
复制
<UserList>
        <User>
            <Username>user123</Username>
            <Address>London</Address>
            <TransactionList>
                <TransactionList>
                    <OrderNumber>1</OrderNumber>
                    <Cost>3683446.6600</Cost>
                </TransactionList>
            </TransactionList>
        </User>
              <User>
            <Username>user123</Username>
            <Address>London</Address>
            <TransactionList>
                <TransactionList>
                    <OrderNumber>3</OrderNumber>
                    <Cost>500</Cost>
                </TransactionList>
            </TransactionList>
        </User>
               <User>
            <Username>user12356</Username>
            <Address>Manchester</Address>
            <TransactionList>
                <TransactionList>
                    <OrderNumber>6</OrderNumber>
                    <Cost>90000</Cost>
                </TransactionList>
            </TransactionList>
        </User>
              <User>
            <Username>user12356</Username>
            <Address>Manchester</Address>
            <TransactionList>
                <TransactionList>
                    <OrderNumber>10</OrderNumber>
                    <Cost>100</Cost>
                </TransactionList>
            </TransactionList>
        </User>
    </UserList>

我想这样订购它,以便每个用户只有一个实例,并且它的相关事务根据用户名和地址分组在一个列表中:

代码语言:javascript
复制
<UserList>
        <User>
            <Username>user123</Username>
            <Address>London</Address>
            <TransactionList>
                <TransactionList>
                    <OrderNumber>1</OrderNumber>
                    <Cost>3683446.6600</Cost>
                </TransactionList>
                <TransactionList>
                    <OrderNumber>3</OrderNumber>
                    <Cost>500</Cost>
                </TransactionList>
            </TransactionList>
        </User>
         <User>
            <Username>user12356</Username>
            <Address>Manchester</Address>
            <TransactionList>
                <TransactionList>
                    <OrderNumber>6</OrderNumber>
                    <Cost>90000</Cost>
                </TransactionList>
                 <TransactionList>
                    <OrderNumber>10</OrderNumber>
                    <Cost>100</Cost>
                </TransactionList>
            </TransactionList>
        </User>
    </UserList> 

我试过用地图做这件事:

代码语言:javascript
复制
Map<String, Map<String,List<User>>> map;

map = userLists.getUserList().stream()
                .collect(Collectors.groupingBy(User::getUserName, Collectors.groupingBy(User::getAddress)));

这是我想要做的,但我想知道是否有更好的方法使用MultiKey地图或类似的东西,然后迭代并列出一个交易列表,如果键匹配的话。

提前谢谢你的帮助。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-10-21 16:16:23

您的方法没有错,第一步是正确的:按userNameaddress对用户进行分组。就我个人而言,我会反转它,因为更可能有更多的用户具有相同的地址,但顺序并不重要,以实现一个核心结果。

我注意到预期的输出类似于List<User>,它减少了具有共同特征的用户(userNameaddress)和连接的transactionList列表。虽然是一个带有复合键的地图(例如。${userName}_${address})似乎很有帮助,我宁愿选择List<User>,顺便说一句,它与预期的输出是一样的。

因此,第二步是迭代所有这些条目,并将Map中的List<User>减少为单个用户。每个内部条目都将与单个用户相关联(因为userNameaddress)。因为-每个人都非常适合这个任务。就这样吧:

代码语言:javascript
复制
Map<String, Map<String,List<User>>> map = userLists.stream()
     .collect(Collectors.groupingBy(
            User::getUserName, 
            Collectors.groupingBy(User::getAddress)));

List<User> list = new ArrayList<>();                 // stored output

map.forEach((userName, groupedByAddress) -> {        // for each 'userName'
    groupedByAddress.forEach((address, users) -> {   // ... and each 'address'
        User userToAdd = new User();                 // ...... create an 'User'
        userToAdd.setUserName(userName);             // ...... with the 'userName'  
        userToAdd.setAddress(address);               // ...... with the 'address'
        users.forEach(user -> {                      // ...... and of each the user's
            userToAdd.getTransactionList()           // .......... concatenate
                .addAll(user.getTransactionList());  // .......... all the transactions
        });
    });
});

Stream是一个很好的分组,以获得一个中间的结果,然而,对于这种进一步的减少将是非常笨拙的。

票数 3
EN

Stack Overflow用户

发布于 2020-10-21 16:42:23

您要寻找的是基于常见用户的名称和地址的合并功能。如果您查看Collectors.toMap API,它提供了类似的功能以及Map的键和值选择。

代码语言:javascript
复制
Collectors.toMap(
        u -> Arrays.asList(u.getUserName(), u.getAddress()),
        Function.identity(),
        User::mergeUsers
));

在合并的地方,看起来就像你期望的那样

代码语言:javascript
复制
static User mergeUsers(User user1, User user2) {
    List<Transaction> overall = new ArrayList<>(user1.getTransactionList());
    overall.addAll(user2.getTransactionList());
    return new User(user1.getUserName(), user1.getAddress(), overall);
}

而且,由于您只是在寻找这个Map的值,所以您的输出有一个完整的解决方案:

代码语言:javascript
复制
Collection<User> users = userLists.getUserList().stream()
        .collect(Collectors.toMap(
                u -> Arrays.asList(u.getUserName(), u.getAddress()),
                Function.identity(),
                User::mergeUsers
        )).values();
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64467151

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档