Использование Java Stream API: один из примеров

· автор BaRoN · На чтение уйдёт 2 минуты · (390 слов)

Всё чаще и чаще вместо того, чтобы писать на Groovy, возвращаюсь к Java. Groovy всё ещё очень часто используется, впрочем, из-за его консоли. groovyConsole — замечательное изобретение, очень многие программы я делаю именно там. Связано моё возвращение к Java-истокам, как нетрудно догадаться, с последними изменениями в Java 8, в частности — Java Stream API. Однако, порой бывают случаи, когда задумываешься и не можешь найти решение. Чтобы таких случаев стало меньше, решил зарегистрироваться на StackExchange в хабе Code Review.

А сюда выложу один из примеров, как Java 8 может сделать жизнь лучше. Итак, оригинальный код:

public class sortByMod {
public static void main(String args[]) throws Exception {

    int a[] = {1,90,2100, 12, 24, 80, 90, 5};
    LinkedList<List<Integer>> a1=sortMod(a);
    for(int i=0; i <a1.size();i++)
            System.out.print(a1.get(i)+" ");
}

public static LinkedList sortMod(int a[]){
    HashMap<Integer, TreeSet<Integer>> map = new HashMap<Integer, TreeSet<Integer>>();
    for(Integer i =0; i<a.length;i++){
        Integer modKey = a[i]%3;
        if(!map.containsKey(modKey)){
            map.put(modKey, new TreeSet<Integer>());
        }
        TreeSet<Integer> temp = map.get(modKey);
        temp.add(a[i]);
        map.put(modKey,temp);
    }
    LinkedList<List<Integer>> result= new LinkedList<List<Integer>>();
    TreeSet<Integer> temp;
    for(int i=0;i<=2;i++) {

        if(map.containsKey(i)){
           temp = map.get(i);
        }
        else return null;

        List<Integer> list = new ArrayList<Integer>(temp);
        result.add(list);

    }
    return result;
}
}

Этот код должен брать изначальный массив и откладывать отдельно элементы, которые делятся на 3 без остатка; на 3 с остатком 1; на 3 с остатком 2.

Что мы видим? Ну, для начала код не отформатирован. Это зря, чем красивее выглядит код, тем проще его читать. Даже тому, кто его пишет. Далее, мы видим return null, который в случае однотипных чисел (например, 1, 4, 7, 10, 13, 16 - т.е. чисел, делящихся на 3 с остатком 1), вернёт null. А это плохо. Ещё в тексте задания было “остатки от деления на 3 или другое число”, а здесь “магические числа” используются дважды: Integer modKey = a[i]%3; и for(int i=0;i<=2;i++) {.

Заменим вторую часть на следующий код:

return map.values().stream().map(ArrayList::new).collect(Collectors.toList());

Одна строчка делает столько же работы, сколько 10 строк оригинального кода. Java Stream API в действии!

А первую можно переписать так:

Map<Integer, List<Integer>> map = Arrays.stream(a).collect(Collectors.groupingBy(t -> t % 3, Collectors.toList()));

или если нужна сортировка, то вот так:

Map<Integer, TreeSet<Integer>> map = Arrays.stream(a).collect(Collectors.groupingBy(t -> t % 3, Collectors.toCollection(TreeSet::new)));

Результат:

public List sortMod(Integer a[]) {
	return Arrays.stream(a).collect(Collectors.groupingBy(t -> t % 3, Collectors.toCollection(TreeSet::new)))
		.values().stream().map(ArrayList::new).collect(Collectors.toList());
}

И его легко переделать на любое число, не только 3. Например:

public List sortMod(Integer a[], int n) {
	return Arrays.stream(a).collect(Collectors.groupingBy(t -> t % n, Collectors.toCollection(TreeSet::new)))
		.values().stream().map(ArrayList::new).collect(Collectors.toList());
}
Полезное