1

I have a list,one field of the element starts with some same chars ,I want to group the list.

class Foo{
        String a;
        String b;
...
}

how can i get the result?

input

List<Foo> list = Lists.newArrayList();
list.add(new Foo("FM11","1"));
list.add(new Foo("FM1122","2"));

output

Map<String,List<String>>
{"FM11":["1","2"],"FM1122":["1","2"]}

I want to use regular expression,if matches,conbine the b to list,key use a. how can I do ?

Pattern.matches(a+".*")
Ding
  • 19
  • 4

3 Answers3

3

The lists for all keys belonging to the same group are identical, so you can save work by collecting only one list for each group before mapping them to the original A values.

Using the Stream API, a solution may look like

List<Foo> list = Arrays.asList(new Foo("FM11","1"), new Foo("FM1122","2"));

Map<String,List<String>> groups = list.stream()
    .collect(Collectors.groupingBy(foo -> foo.getA().substring(0, 4),
        Collectors.mapping(Foo::getB, Collectors.toList())));

Map<String,List<String>> result = list.stream()
    .collect(Collectors.toMap(Foo::getA, foo -> groups.get(foo.getA().substring(0, 4))));

result.forEach((k,v) -> System.out.println(k+" -> "+v));
FM1122 -> [1, 2]
FM11 -> [1, 2]

You can do it even more efficiently when not using the Stream API. With a loop, we can do both operations in one go, halving the work of looking up the groups in the map.

Map<String,List<String>> groups = new HashMap<>(), result = new HashMap<>();

for(Foo foo: list) {
  List<String> bList
      = groups.computeIfAbsent(foo.getA().substring(0, 4), x -> new ArrayList<>());
  bList.add(foo.getB());
  result.put(foo.getA(), bList);
}

The result is identical.

Holger
  • 285,553
  • 42
  • 434
  • 765
1

Try this out

List<Foo> list = new ArrayList<>();
list.add(new Foo("FM11","1"));
list.add(new Foo("FM11","2"));
Map<String,List<String>> result =  list.stream()
    .collect(Collectors.toMap(foo -> foo.getA(), foo -> list.stream()
        .filter(foo2-> foo2.getA().substring(0, 4).equals(foo.getA().substring(0, 4)))
        .map(Foo::getB)
        .collect(Collectors.toList())));

Output

{FM1122=[1, 2], FM11=[1, 2]}
Nidhish Krishnan
  • 20,593
  • 6
  • 63
  • 76
  • I think OP wants to use `Foo.a` as key and add all `Foo.b` s whose `Foo.a` s have the same prefix as the current key to the values list. His list has only two entries and dosen't include for example `list.add(new Foo("FM11","2"));` whic you have included. – Eritrean Aug 23 '19 at 11:55
  • @Eritrean Hm...not sure what the actual requirement is, since OP just simply says `grouping by similar key with java-lambda` – Nidhish Krishnan Aug 23 '19 at 12:15
  • 1
    That’s very inefficient (quadratic time complexity). It also creates a lot of unnecessary substring instances. Instead of `foo2.getA().substring(0, 4).equals(foo.getA().substring(0, 4))`, you can use `foo2.getA().regionMatches(0, foo.getA(), 0, 4)`. But it doesn’t solve the general problem of performing `list.size() × list.size()` operations… – Holger Aug 23 '19 at 14:05
0

For each foo in the list, you can iterate over the list again and filter those that have the same prefix for the attribute a out. Temporarily save it to a temp variable and add it to your map.

    List<Foo> list = new ArrayList<>();
    list.add(new Foo("FM11","1"));
    list.add(new Foo("FM1122","2"));
    Map<String,List<String>> map = new HashMap<>();
    list.forEach(f -> {
        List<String> temp = list.stream()
                .filter(e->e.getA().substring(0, 4).equals(f.getA().substring(0, 4)))
                .map(e->e.getB())
                .collect(Collectors.toList());

        map.computeIfAbsent(f.getA(), k -> new ArrayList<>()).addAll(temp);
    });
    System.out.println(map);
Eritrean
  • 15,851
  • 3
  • 22
  • 28