Toon posts:

Java 8 Streams, member wijzigen met map()

Pagina: 1
Acties:

Vraag


  • steveman
  • Registratie: Mei 2001
  • Laatst online: 17:24

steveman

Comfortabel ten onder

Topicstarter
Een tijdje terug kwam ik een bug tegen in m'n code, dit heb ik kunnen reduceren tot het volgende waar het probleem optreedt. Ik heb er al een tijd mee lopen stoeien, maar het waarom ontgaat me.

Het doel is om een threshold toe te passen op een rijtje getallen, getallen die niet voldoen worden NaN, dat werkt. En dan wordt er nog een flag bijgehouden om te zien of er overschrijdingen waren. Het wijzigen van die flag geeft problemen.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class Why {
    public static void main(String[] args)  {
        System.out.println(System.getProperty("java.version"));
        // Works as expected
        Stream<Double> data = Stream.of(Double.valueOf(1.0), Double.valueOf(2.0), Double.valueOf(3.0),
                Double.valueOf(4.0), Double.valueOf(5.0));

        Flag flag = new Flag();

        System.out.println("Result: " + data.map(i -> valueOverThree(i, flag)).collect(Collectors.toList()));
        System.out.println("Flag: " + flag.getFlag() + "\n");

        
        
        // Flag remains false... Why?
        final Stream<Double> data2 = Stream.of(Double.valueOf(1.1), Double.valueOf(2.1), Double.valueOf(2.9),
                Double.valueOf(4.1), Double.valueOf(5.1));

        Flag flag2 = new Flag();
        
        Stream<Double> result = data2.map(a -> valueOverThree(a, flag2));
        Object[] outputWrapper = valueOverThreeWithFlag(result);
        
        System.out.println("Result: " + ((Stream<Double>) (outputWrapper[0])).collect(Collectors.toList()));
        System.out.println("Flag: " +outputWrapper[1]);
    }

    public static Object[] valueOverThreeWithFlag(final Stream<Double> data) {
        Flag flag = new Flag();
        flag.setFlag(false);
        return new Object[] { data.map(a -> valueOverThree(a, flag)), Boolean.valueOf(flag.getFlag()) };
    }

    static Double valueOverThree(final Double input, Flag flag) {
        if (input.doubleValue() > 3.0) {
            flag.setFlag(true);
            System.out.println("Setting the flag to true: " + input);
            return Double.NaN;
        }

        return input;
    }
}

//De genoemde Flag 
public class Flag {
    private boolean flag;

    /**
     * @return the flag
     */
    public boolean getFlag() {
        return flag;
    }

    /**
     * @param flag the flag to set
     */
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}



De output van het programma verwart mij:

Setting the flag to true: 4.0
Setting the flag to true: 5.0
Result: [1.0, 2.0, 3.0, NaN, NaN]
Flag: true //mooi!

Setting the flag to true: 4.1
Setting the flag to true: 5.1
Result: [1.1, 2.1, 2.9, NaN, NaN]
Flag: false //heh?

Ik snap niet wat er nou anders is aan die tweede case, waarbij het flag object gemaakt wordt in de methode.

Met Java 10.0.2 overigens (Ik zat nog op 8, maar dat werkte hetzelfde).
Met StreamEx (https://github.com/amaembo/streamex) ook hetzelfde resultaat.
Inlinen van de objecten die het array in gaan op line 31 hielp (gelukkig) ook niet.

Tips?

"Take the risk of thinking for yourself. Much more happiness, truth, beauty, and wisdom will come to you that way." -Christopher Hitchens | In memoriam? 🏁 ipv kruis!

Beste antwoord (via steveman op 25-09-2018 09:22)


  • Feanathiel
  • Registratie: Juni 2007
  • Niet online

Feanathiel

Cup<Coffee>

De collect(Collectors.toList()) forceert evaluatie van de expressie ervoor. data.map(a -> valueOverThree(a, flag)) doet opzich zelf niks (lazy, zet maar een breakpoint), dus als je direct daarna Boolean.valueOf(flag.getFlag()) opvraagt, dan is valueOverThree nog nooit uitgevoerd en de flag dus false. Uiteindelijk wordt de flag wel op true gezet, maar dan heb je 'm al bevraagd, en heb je er niks meer aan.

[Voor 18% gewijzigd door Feanathiel op 24-09-2018 22:29]

Alle reacties


  • ValHallASW
  • Registratie: Februari 2003
  • Niet online
Ja, https://plugins.jetbrains...9696-java-stream-debugger, of je code iets anders uitschrijven:

Java:
1
2
3
4
5
Flag flag1 = new Flag();
Flag flag2 = new Flag();
        
List<Double> result1 = data2.map(a -> valueOverThree(a, flag1)).collect(Collectors.toList())
List<Double> result2 = result1.map(a -> valueOverThree(a, flag2)).collect(Collectors.toList());


Je zult dan zien dat er in result1 geen waarden groter dan 3 meer staan -- die heb je immers door NaN vervangen. flag2 wordt dan ook niet op true gezet.

Acties:
  • Beste antwoord
  • +1Henk 'm!

  • Feanathiel
  • Registratie: Juni 2007
  • Niet online

Feanathiel

Cup<Coffee>

De collect(Collectors.toList()) forceert evaluatie van de expressie ervoor. data.map(a -> valueOverThree(a, flag)) doet opzich zelf niks (lazy, zet maar een breakpoint), dus als je direct daarna Boolean.valueOf(flag.getFlag()) opvraagt, dan is valueOverThree nog nooit uitgevoerd en de flag dus false. Uiteindelijk wordt de flag wel op true gezet, maar dan heb je 'm al bevraagd, en heb je er niks meer aan.

[Voor 18% gewijzigd door Feanathiel op 24-09-2018 22:29]


  • steveman
  • Registratie: Mei 2001
  • Laatst online: 17:24

steveman

Comfortabel ten onder

Topicstarter
Ah, juist! Nou zo blijkt maar, m'n J8-boeken die vanmiddag aankomen zijn geen overbodige luxe :)

"Take the risk of thinking for yourself. Much more happiness, truth, beauty, and wisdom will come to you that way." -Christopher Hitchens | In memoriam? 🏁 ipv kruis!


  • Hydra
  • Registratie: September 2000
  • Laatst online: 15:50
Afgezien van het antwoord: dat soort state modifications op je input en dan gewoon hetzelfde object doorsturen wil je vermijden. Deze nieuwe Java 8 spullen zijn gestoeld op het functioneel programmeren denken waarbij je objecten in princiepe immutable zijn; je 'map' levert dus normaliter een nieuw object op. Dat maakt de code een stuk simpeler om over te redeneren, helemaal als je concurrency moet ondersteunen.

https://niels.nu



Tweakers maakt gebruik van cookies

Tweakers plaatst functionele en analytische cookies voor het functioneren van de website en het verbeteren van de website-ervaring. Deze cookies zijn noodzakelijk. Om op Tweakers relevantere advertenties te tonen en om ingesloten content van derden te tonen (bijvoorbeeld video's), vragen we je toestemming. Via ingesloten content kunnen derde partijen diensten leveren en verbeteren, bezoekersstatistieken bijhouden, gepersonaliseerde content tonen, gerichte advertenties tonen en gebruikersprofielen opbouwen. Hiervoor worden apparaatgegevens, IP-adres, geolocatie en surfgedrag vastgelegd.

Meer informatie vind je in ons cookiebeleid.

Sluiten

Toestemming beheren

Hieronder kun je per doeleinde of partij toestemming geven of intrekken. Meer informatie vind je in ons cookiebeleid.

Functioneel en analytisch

Deze cookies zijn noodzakelijk voor het functioneren van de website en het verbeteren van de website-ervaring. Klik op het informatie-icoon voor meer informatie. Meer details

janee

    Relevantere advertenties

    Dit beperkt het aantal keer dat dezelfde advertentie getoond wordt (frequency capping) en maakt het mogelijk om binnen Tweakers contextuele advertenties te tonen op basis van pagina's die je hebt bezocht. Meer details

    Tweakers genereert een willekeurige unieke code als identifier. Deze data wordt niet gedeeld met adverteerders of andere derde partijen en je kunt niet buiten Tweakers gevolgd worden. Indien je bent ingelogd, wordt deze identifier gekoppeld aan je account. Indien je niet bent ingelogd, wordt deze identifier gekoppeld aan je sessie die maximaal 4 maanden actief blijft. Je kunt deze toestemming te allen tijde intrekken.

    Ingesloten content van derden

    Deze cookies kunnen door derde partijen geplaatst worden via ingesloten content. Klik op het informatie-icoon voor meer informatie over de verwerkingsdoeleinden. Meer details

    janee