java 8 lambda — использовать поток несколько раз

#java #lambda

#java #лямбда

Вопрос:

Можно ли избежать создания потока внутри текущего потока из той же коллекции, что и в примере ниже, для сбора некоторых данных (listOfA используется два раза для создания потока)?

 List<A> listOfA = Arrays.asList(new A(1L, "A1", "V1"), new A(2L, "A2", "V1"), new A(1L, "A1", "V2"));

List<B> listOfB = listOfA.stream().map(r -> new B(r.getId(), r.getName(),
            listOfA.stream().filter(r2 -> r.getId().equals(r2.getId())).map(A::getVal).collect(toSet())
    )).distinct().collect(toList());

class A {
     private final Long id;
     private final String name;
     private final String val;

     A(Long id, String name, String val) //constructor
     //getters

}

class B {
     private final Long id;
     private final String name;
     private final Set<String> values;

     B(Long id, String name, Set<String> values) //constructor
     //getters


     @Override
     public boolean equals(Object o) {
          ...
          return id.equals(a.id);
     }
     //hashCode
 }
  

Конечным результатом должен быть список из 2 объектов:

B{id= 1, name=’A1′, values= [V1, V2]}

B{id= 2, name=’A2′, values= [V1]

Заранее спасибо!

Ответ №1:

Я не уверен, на что направлен ваш вопрос. Если вопрос в том, какие минимальные изменения необходимы, чтобы избежать повторного создания потока, тогда я должен ответить: я не знаю. Однако, похоже, ваш подход слишком сложен. Цепочку map , collect , filter , distinct и collect то, что там построено, действительно трудно понять.

После короткой напыщенной речи…

Возможно, мой страх, что все будущие Java-программы будут выглядеть так и, следовательно, станут полностью недоступными для обслуживания, не оправдан. Возможно, нужно просто «привыкнуть» к этому стилю программирования. Возможно, есть короткий период, когда люди слишком стремятся использовать новые возможности языка, и рано или поздно он вернется к «нормальному» стилю (и «здоровому» уровню функциональных элементов). Но я лично считаю, что такой метод, как createBsByMergingTheValuesOfAs() здесь, был бы достаточным и уместным.

… Я хотел бы предложить использовать выделенный Collector , который уже предлагает большую часть инфраструктуры для сокращения изменяемых параметров, которую вы, похоже, эмулируете с помощью этой цепочки операций:

 import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class StreamCollectTest
{
    public static void main(String[] args)
    {
        List<A> listOfA = Arrays.asList(
            new A(1L, "A1", "V1"), 
            new A(2L, "A2", "V1"), 
            new A(1L, "A1", "V2"));

        Map<Long, B> result = listOfA.stream().collect(
            Collectors.toConcurrentMap(

                // The "id" will be the key of the map
                a -> a.getId(), 

                // The initial value stored for each key will be a "B"
                // whose set of values contains only the element of 
                // the corresponding "A"
                a -> new B(a.getId(), a.getName(), 
                    new LinkedHashSet<String>(Collections.singleton(a.getVal()))),

                // Two "B"s with the same key will be merged by adding
                // all values from the second "B" to that of the first
                (b0,b1) -> { b0.values.addAll(b1.values); return b0; }));

        System.out.println(result);
    }

    static class A
    {
        private final Long id;
        private final String name;
        private final String val;

        A(Long id, String name, String val)
        {
            this.id = id;
            this.name = name;
            this.val = val;
        }

        public Long getId()
        {
            return id;
        }

        public String getName()
        {
            return name;
        }

        public String getVal()
        {
            return val;
        }
    }

    static class B
    {
        private final Long id;
        private final String name;
        private final Set<String> values;

        B(Long id, String name, Set<String> values)
        {
            this.id = id;
            this.name = name;
            this.values = values;
        }

        @Override
        public String toString()
        {
            return id "," name "," values;
        }
    }
}
  

Он печатает

 {1=1,A1,[V1, V2], 2=2,A2,[V1]}
  

Таким values() образом, результирующая карта должна быть именно тем, что вы ищете.