Java对象列表:聚合

时间:2014-08-10 06:25:25

标签: java

我有一个案例需要汇总Beans/Objects列表以显示其总特征。让我先解释一下我的要求。我有List个人详细信息,如下所示:

    Person  Bank  Balance
    ---------------------
    Sam      GS   200
    Sam      JP   200
    Sam      WF   200
    John     GS   200
    John     JP   200
    Robin    JP   200
    Robin    JP   200

我想汇总每个人的余额。

    Sam           700   <---- Key
    ------------------
        Sam      GS   200
        Sam      JP   300
        Sam      WF   200

    John          500   <---- Key
    ------------------
        John     GS   300
        John     JP   200

    Robin         200   <---- Key
    ------------------
        Robin    JP   100
        Robin    JP   100

现在,让我们切换到我的代码 - List这样的对象,我需要将它们放在Map中。关键是聚合细节,其值将是详细列表。这是我的尝试,虽然不是那么好:

public Map<Bean, List<Bean>> getAggregation(List<Bean> beans)
{
    Map<Bean, ArrayList<Bean>> aggreagtedBeans = new HashMap<Bean, new ArrayList<Bean>>();

    for(Bean bean : beans)
    {
        String name = bean.getName();
        boolean presentALready = false;
        Bean correspondingKey = null;
        for(Bean key :aggreagtedBeans.keySet())
        {
            if(key.getName().equals(name))
            {
                presentALready = true;
                correspondingKey = key;
                }
            }

            if(presentALready)
            {
                aggreagtedBeans.put(correspondingKey, aggreagtedBeans.get(correspondingKey).add(bean));
            }

            else
            {
                aggreagtedBeans.put(bean, aggreagtedBeans.get(correspondingKey).add(bean));
            }

        }
    return aggreagtedBeans;     
}

问题:

即使使用此Map方法,密钥也会得到修复,因此无法在为特定人员添加每行时更新余额。

限制:

我知道这种用例非常适合数据库order by子句,但我不能使用它们。这些是Java Objects

此外,如果您认为我应该根据我的使用情况使用不同的数据结构,请建议,如果可能请提供代码段。

编辑:

附加Bean类代码以供参考:

public class Bean 
{
    String name;
    String bank;
    int balance;

    // constructors and getters
} 

5 个答案:

答案 0 :(得分:2)

由于Java 8很棒,所以无法帮助自己:

public static void main(String [] args) {
    List<Bean> list = new ArrayList<>();
    list.add(new Bean("John", 10));
    list.add(new Bean("Sam", 666));
    list.add(new Bean("Sam", 9));
    list.add(new Bean("John", 1));
    list.add(new Bean("John", 7));

    Map<String, Integer> sum = list.stream().collect(Collectors.groupingBy(Bean::getName, Collectors.summingInt(Bean::getBalance)));
    sum.entrySet().forEach(x -> list.add(new Bean(x.getKey(),x.getValue(), true)));
    list.sort((x,y) -> {
        int nameComp = x.getName().compareTo(y.getName());
        if (nameComp == 0)
            return x.isSum() ? -1 : 1;
        return nameComp;
    });
    list.forEach(System.out::println);
}

// Bean class with no "bank" variable, but with a new constructor and overloaded .toString()
static class Bean {
    private String name;
    private int balance;
    private final boolean isSum;

    Bean(String name, int balance, boolean isSum) {
        this.isSum = isSum;
        this.name = name;
        this.balance = balance;
    }

    Bean(String name, int balance) {
        this.isSum = false;
        this.name = name;
        this.balance = balance;
    }

    public String getName() { return name; }
    public int getBalance() { return balance; }
    public boolean isSum() { return isSum; }
    @Override
    public String toString() { return name + " | " + balance + (isSum() ? " *Sum*" : ""); }
}

输出:

  

约翰| 18 Sum

     

约翰| 10

     

约翰| 1

     

约翰| 7

     

Sam | 675 总和

     

Sam | 666

     

Sam | 9

答案 1 :(得分:1)

积累余额需要的是

Map<String, Integer> name2balance = new HashMap<>();

Bean在地图中不是一个非常好的键,你试图为一个人积累数据,因为你在名字和Bean(名称,银行,余额)之间有一个1:n的关系。

特别是对于潜在的易变数据(余额!),这可能会导致麻烦。

答案 2 :(得分:1)

公共类JavTest {

public static void main(String[] args) {

    List<Bean> list = new ArrayList<>();
    list.add(new Bean("Sam", "GS",  200));
    list.add(new Bean("Sam", "JP",  300));
    list.add(new Bean("Sam", "WF",  200));
    list.add(new Bean("John", "GS",  300));
    list.add(new Bean("John", "JP",  200));
    list.add(new Bean("Robin", "JP",  100));
    list.add(new Bean("Robin", "JP",  100));


    HashMap<Bean, ArrayList<Bean> > beans = (HashMap<Bean, ArrayList<Bean>>) getAggregation(list);

    Set<Bean> sb = beans.keySet();

    for(Bean b : sb){
        System.out.println(b.getName() + " " + b.bank + " " + b.balance);
        for(Bean bb : beans.get(b)){
            System.out.println(bb.getName() + " " + bb.bank + " " + bb.balance);
        }
        System.out.println("---------------");
    }
}

public static Map<Bean, ArrayList<Bean>> getAggregation(List<Bean> beans)
{
    //ArrayList<Bean> al = new ArrayList<Bean>();
    HashMap<Bean, ArrayList<Bean> > aggreagtedBeans = new HashMap<Bean, ArrayList<Bean>>();

    for(Bean bean : beans)
    {

        String name = bean.getName();

        Set<Bean> keys = aggreagtedBeans.keySet();
        boolean found = false;
        Bean y = null;

        for(Bean x : keys){
            if(x.name == name){
                y = x;
                found = true;
                break;
            }
        }

        if(found == true){
            ArrayList<Bean> al = aggreagtedBeans.get(y);
            al.add(bean);

            Bean newBean = new Bean(y.name, bean.bank, y.balance + bean.balance);
            aggreagtedBeans.remove(y);

            aggreagtedBeans.put(newBean, al);               

        }else{

            ArrayList<Bean> tmp = new ArrayList<Bean>();
            tmp.add(bean);
            aggreagtedBeans.put(bean, tmp);
        }
    }



    return aggreagtedBeans;     
}

}

输出:

Sam WF 700 山姆GS 200 Sam JP 300

Sam WF 200

Robin JP 200 罗宾JP 100

Robin JP 100

John JP 500 John GS 300

John JP 200

它应该可以工作,但是使用Bean作为密钥效率不高,因为每次要编辑它时都必须将其删除及其数组列表然后重新添加它们!

答案 3 :(得分:1)

尝试这样的事情假设一个人只能在一个银行中拥有一个帐户。

        int balOfSam = 0;
        int balOfJohn = 0;
        int balOfRobin = 0;
        for(Bean bean : list){
                map.put(bean.getName()+bean.getBank(),bean.getBalance());
        }
        Set<String> set = map.keySet();
        for(String key :set){
            if(key.startsWith("Sam"))
                balOfSam+=map.get(key);
            else if(key.startsWith("John"))
                balOfJohn+=map.get(key);
            else
                balOfRobin+=map.get(key);
        }
        System.out.println(balOfJohn);
        System.out.println(balOfSam);
        System.out.println(balOfRobin);

答案 4 :(得分:1)

使用java 8:

List<Bean> allData = Arrays.asList(
    new Bean("Sam", "GS", 200),
    new Bean("Sam", "JP", 200),
    new Bean("Sam", "WF", 200),
    new Bean("John", "GS", 200),
    new Bean("John", "JP", 200),
    new Bean("Robin", "JP", 200),
    new Bean("Robin", "JP", 200)
);

allData.stream().collect(
        groupingBy(Bean::getName)
).forEach((name, dataForPerson) -> {   // dataForPerson is a List<Bean> for the name

    int totalForName = dataForPerson.stream()
            .mapToInt(Bean::getBalance)
            .sum(); 

    System.out.printf(       /* print header for each person */
            "%n%-14s%d%n-----------------%n", 
            name, 
            totalForName);

    dataForPerson.forEach(    /* print each entry for the person *//
            b->System.out.printf(
                    "%7s%5s%5s%n", 
                    b.getName(), 
                    b.getBank(), 
                    b.getBalance()
            )
    );      
});

输出:

John          400
-----------------
   John   GS  200
   John   JP  200

Robin         400
-----------------
  Robin   JP  200
  Robin   JP  200

Sam           600
-----------------
    Sam   GS  200
    Sam   JP  200
    Sam   WF  200
相关问题