Cookies op Tweakers

Tweakers is onderdeel van DPG Media en maakt gebruik van cookies, JavaScript en vergelijkbare technologie om je onder andere een optimale gebruikerservaring te bieden. Ook kan Tweakers hierdoor het gedrag van bezoekers vastleggen en analyseren. Door gebruik te maken van deze website, of door op 'Cookies accepteren' te klikken, geef je toestemming voor het gebruik van cookies. Wil je meer informatie over cookies en hoe ze worden gebruikt? Bekijk dan ons cookiebeleid.

Meer informatie
Toon posts:

Java 8 Streams, member wijzigen met map()

Pagina: 1
Acties:

Vraag


  • steveman
  • Registratie: mei 2001
  • Laatst online: 22:04

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: 22:04

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: 16:28
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



Apple iPad Pro (2021) 11" Wi-Fi, 8GB ram Microsoft Xbox Series X LG CX Google Pixel 5a 5G Sony XH90 / XH92 Samsung Galaxy S21 5G Sony PlayStation 5 Nintendo Switch Lite

Tweakers vormt samen met Hardware Info, AutoTrack, Gaspedaal.nl, Nationale Vacaturebank, Intermediair en Independer DPG Online Services B.V.
Alle rechten voorbehouden © 1998 - 2021 Hosting door True