Tworzenie listy obiektów z listy innych obiektów

0

Cześć wszystkim !
Mam 2 klasy: Car i DoubleCar z jednym polem price. Chciałbym najlepiej w strumieniu (ale nie wiem czy to możliwe), ewentualnie inaczej stworzyć listę obiektów DoubleCar w taki sposób, że iterując po liście obiektów Car obiekt DoubleCar będzie tworzony przez sume wartości price z aktualnego obiektu Car i wartości price z kolejnego obiektu Car na liście.

@AllArgsConstructor
public class Car {
    int price;
}


@Value
public class DoubleCar {
    int price;

    public static DoubleCar createDoubleCar(int price1, int price2){
        return new DoubleCar(price1 + price2);
    }
}

public class Main {
    public static void main(String[] args) {
        List<Car> cars = List.of(new Car(1), new Car(2), new Car(1), new Car(1));

        List<DoubleCar> doubleCars = cars.stream()
                //tu DoubleCar.createDoubleCar(car.price, car.price) w drugim argumencie powinno znależć się
                // price z kolejnego obiektu Car
                .map(car -> DoubleCar.createDoubleCar(car.price, car.price))
                .collect(Collectors.toList());
    }
}
0
witu12 napisał(a):

Cześć wszystkim !
Mam 2 klasy: Car i DoubleCar z jednym polem price. Chciałbym najlepiej w strumieniu (ale nie wiem czy to możliwe), ewentualnie inaczej stworzyć listę obiektów DoubleCar w taki sposób, że iterując po liście obiektów Car obiekt DoubleCar będzie tworzony przez sume wartości price z aktualnego obiektu Car i wartości price z kolejnego obiektu Car na liście.

Pierwsze pytanie jakie się pojawia, to po co w ogóle chcesz to zrobić. Wydawać by się mogło, że już tak stworzona lista, w której co drugie auto ma się zachować inaczej, już na starcie jest błędem, np. niepoprawne sparsowanie czegoś, etc.

Musisz też się zastanowić, jaki jest spodziewany efekt, jeśli masz nieparzystą liczbę samochodów, co się powinno wtedy stać? Wyjątek? Zwrócić DoubleCar utworzony z jednego auta zamiast dwóch?

Ale jeśli koniecznie chcesz zrobić coś takiego, to:

AtomicInteger counter = new AtomicInteger();
stream
  .collect(groupingBy(x -> counter.getAndIncrement() / 2))
  .values()
  .forEach(cars -> {
      Car first = cars.get(0);
      Car second = cars.get(1); // co jeśli jest parzysta liczba samochodów?
      
      DoubleCar doubleCar = new DoubleCar(first, second);
      // zrób z nim co chcesz
  });
1

Cały pomysł ze streamami przeznaczony jest do sytuacji, że "kursor" wskazuje jeden bieżący element. Wtedy jest benefit w krótkości takiego kodu, czytelności
Porzucenie tego założenia, to zaczyna się jedzenie zupy widelcem.

Przychodzą sytuacje, że najstarsza forma indeksowanej pętli jest najkrótsza i najczytelniejsza (sorry piszący zawsze "nowocześnie" ...)

for(i=0; i< list.size(); i+=2)
{
   new DoubleCarWhateveIs ( list.get(i), list.get(i+1) );
}

Przy bardzo ważnym zastrzeżeniu @Riddle nt nieparzystych

1

Guava:


public static DoubleCar createDoubleCar(List<Car> cars) {
    return new DoubleCar(cars.stream().mapToInt(c -> c.price).sum());
}

...

List<DoubleCar> doubleCars = Lists.partition(cars, 2)
                .stream()
                .map(DoubleCar::createDoubleCar)
                .toList();

Akurat tutaj w przypadku nieparzystej listy elementów tworzę sobie DoubleCar z ostatniego, ale można zaimplementować coś innego tam.

0
ZrobieDobrze napisał(a):

Cały pomysł ze streamami przeznaczony jest do sytuacji, że "kursor" wskazuje jeden bieżący element. Wtedy jest benefit w krótkości takiego kodu, czytelności
Porzucenie tego założenia, to zaczyna się jedzenie zupy widelcem.

Wszystko fajnie i pięknie, tyle tylko, że na streamach też bardzo łatwo coś takiego osiągnąć:

   public static void main(final String[] args) {
        List<Integer> elements = Arrays.asList(1, 2, 3, 4, 5, 6);

        IntStream.iterate(0, i->i+2)
                .takeWhile(i -> i<elements.size()-1)
                .mapToObj(index -> new IntPair(elements.get(index), elements.get(index + 1)))
                .forEach(System.out::println);
    }
    
    private static class IntPair {
        private final int first;
        private final int second;

        public IntPair(int first, int second) {
            this.first = first;
            this.second = second;
        }

        @Override
        public String toString() {
            return "[" + first + ";" + second + "]";
        }
    }

IMO zapis od góry do dołu jest czytelniejszy od starych pętli. Co prawda pewnie każdy programista tyle się naczytał tych for'ów, że nie robi mu różnicy ten głupi szyk, ale rozbicie generowania elementów i warunku stopu jest dla mnie czytelniejsze niż for.

Ogólnie to starych for'ów używam już bardzo rzadko - tam, gdzie liczy się wydajność. Zadziwiająco często za to zdarza mi się pisać while / do-while :/

0
witu12 napisał(a):

Mam 2 klasy: Car i DoubleCar z jednym polem price. Chciałbym najlepiej w strumieniu (ale nie wiem czy to możliwe), ewentualnie inaczej stworzyć listę obiektów DoubleCar w taki sposób, że iterując po liście obiektów Car obiekt DoubleCar będzie tworzony przez sume wartości price z aktualnego obiektu Car i wartości price z kolejnego obiektu Car na liście.

@witu12: No to żeby rozwiązać konflikt, musiałbyś nam napisać jaki jest spodziewany efekt przez Ciebie, dla wejścia: List.of(new Car(1), new Car(2), new Car(3), new Car(1))?

Czy spodziewanym wynikiem dla Ciebie byłoby:

  • List.of(new DoubleCar(1,2), new DoubleCar(3, 1))?
  • List.of(new DoubleCar(1,2), new DoubleCar(2, 3), new DoubleCar(3, 1))?
  • Coś innego? Napisz jaki.

1 użytkowników online, w tym zalogowanych: 0, gości: 1