简体   繁体   中英

How do I merge Objects in a ArrayList?

I have an arraylist of objects called Variable (ArrayList-Variable-) (The - is instead of the <, which otherwise will trigger the quote). This Variable object has two fields: name and domain, where the name is a String and the domain is an array of int. I want to merge the variables that have the same name, specifically I want to have a single variable with that name and with domain equal to the union of all the domains of the variables with the same name (without duplicates).

For example:

ArrayList: [VarA: 1, VarA: 6, VarB: 9, VarB: 3, VarA: 4, VarC: 2, VarC: 1]

What I would like to have is: [VarA: {1,6,4}, VarB: {9,3}, VarC: {2,1}]

How can I achieve that?

You can simply use HashMap to store such data Let say variable for your ArrayList is arr then:-

HashMap<String, Set<Integer>> map = new HashMap<>();
for(Variable var:arr) {
   Set<Integer> set = map.getOrDefault(var.name, new HashSet<Integer>());
   set.add(var.domain);
   map.put(var.name, set);
}

Then map ( HashMap ) will contain desired results which you can convert if required like again to ArrayList .

I would use a Hashmap of list to achieve that.

for (Tuple item: list) {
    if (!map.containsKey(item.key)) {
        map.put(item.key, new LinkedList<String>());
    }
    map.get(item.key).add(item.value);
}

Then using map.values(), you can recover a collection of the value Lists.

With Java 8 Stream you can do it as below:

Map<String, Set<Integer>> result = v.stream().collect(
        Collectors.groupingBy(Variable::getDomain, 
            HashMap::new, Collectors.mapping(Variable::getVal, Collectors.toSet()))
      );

Test Code:

List<Variable> v = new ArrayList<Variable>();
v.add(new Variable("A", 1));
v.add(new Variable("A", 2));
v.add(new Variable("B", 4));
v.add(new Variable("C", 7));
v.add(new Variable("C", 7));

 Map<String, Set<Object>> result = v.stream().collect(
    Collectors.groupingBy(Variable::getDomain, 
        HashMap::new, Collectors.mapping(Variable::getVal, Collectors.toSet()))
  );
System.out.println(result);

Output: {A=[1, 2], B=[4], C=[7]}

Variable Class:

class Variable {

  private String domain;

  private int val;

  public Variable(String domain, int val) {
    this.domain = domain;
    this.val = val;
  }

  /**
   * @return the domain
   */
  public String getDomain() {
    return domain;
  }

  /**
   * @param domain
   *          the domain to set
   */
  public void setDomain(String domain) {
    this.domain = domain;
  }

  /**
   * @return the val
   */
  public int getVal() {
    return val;
  }

  /**
   * @param val
   *          the val to set
   */
  public void setVal(int val) {
    this.val = val;
  }

}

You already have a list? You can use groupingBy.

Map<String, List<Integer>> grouped = list.stream().collect( Collectors.groupingBy( Variable::getGroup ) );
  • this creates a List of some instances (records) that have duplicate values in the array for similar names.
  • It uses a hashMap to store the name and domain by taking advantage of computeIfAbsent to first create the set if it doesn't exist, otherwise, add the arrays to it. The purpose of the set is to eliminate duplicates.
// your list of objects each with a name and Integer[] domain   
List<Variable> vars = List.of(new Variable("var1", 1, 2, 3),
        new Variable("var2", 4, 5, 6), new Variable("var1", 1, 9, 10),
        new Variable("var5", 1, 6, 8), new Variable("var2", 4, 8, 9));
    
Map<String, Set<Integer>> result = new HashMap<>();
for (Variable v : vars) {
    result.computeIfAbsent(v.name, k -> new HashSet<>())
            .addAll(Arrays.asList(v.domain));
}
    
result.entrySet().forEach(System.out::println);

prints

var5=[1, 6, 8]
var2=[4, 5, 6, 8, 9]
var1=[1, 2, 3, 9, 10]

The Variable class per your description.

class Variable {
    private String name;
    private Integer[] domain;
    public Variable(String name, Integer... domain) {
        super();
        this.name = name;
        this.domain = domain;
    }
    public String getName() {
        return name;
    }
    public Integer[] getDomain() {
        return domain;
    }   
}

You can also do it using streams. I chose to use the flatMapping collector to combine the domain arrays into a set for each name.

Map<String,Set<Integer>> result = vars.stream().collect(
        Collectors.groupingBy(v->v.getName(),
           Collectors.flatMapping(
                   v->Arrays.stream(v.getDomain()),
                           Collectors.toSet())));

This yields the same as above. Both of the above examples assume an Integer array to hold the domain items. A List<Integer> would be even better. Primitive arrays aren't readily supported by Arrays.asList or stream operations but there are workarounds.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM