Bonjour,
je viens de passer sur le JDK8_u20 et j'ai immédiatement rencontré un problème concernant HashMap : dans un cas de figure très particulier, la map indique qu'elle ne contient pas des clés alors qu'elles sont insérées quelques lignes plus haut, que la map n'est pas vidée entre temps et qu'une itération sur ses entrées indique que les clés sont bien présentes (ainsi que les valeurs associées). Alors du coup bien sur, la map retourne une valeur null pour une clé donne ce qui est très gênant.

Pour le background : nous avons des onglets JavaFX (Tab) en tant que clé et un objet TabInfo en tant que valeur. Le contenu de chaque onglet est chargé dynamiquement lorsque l'utilisateur affiche l'onglet dans l'UI (de nombreux graphes sont affichés dont cela prend trop de temps de le générer a l'avance, ici on les génère dans une tache de fond et un contrôle affichant une animation est présent a l’écran tant que le graphique n'a pas été finalisé).
Le problème se passe bien avant que la tache de fond ne soit exécutée, directement à l'initialisation de la table.

Ce bout de code fonctionnait sans problème aucun jusqu'au JDK8_u11 (que je viens de réinstaller pour vérifier que tout passe sans problème). De plus, je n’arrive pas a reproduire le problème dans un test unitaire, ce qui me laisse perplexe.

Un passage en débogage montre que le code de la fonction final Node<K,V> getNode(int hash, Object key) de la classe HashMap échoue sur son test :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
Mais impossible de voir les valeur car le JDK a été compilé sans info de débogage. Cependant, il semble cependant que le problème se pose car la fonction hashCode() de la classe Tab a été surchargée dans le JDK8_u20:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
@Override public int hashCode() {
        int result = selected != null ? selected.hashCode() : 0;
        result = 31 * result + (tabPane != null ? tabPane.hashCode() : 0);
        result = 31 * result + (text != null ? text.hashCode() : 0);
        result = 31 * result + (graphic != null ? graphic.hashCode() : 0);
        result = 31 * result + (content != null ? content.hashCode() : 0);
        return result;
    }
Les versions précédentes de hashCode() utilisaient la fonction définie dans Object.

Voici le code brut de l'initialisation :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
63
64
65
66
67
68
 
    private static class TabInfo {
 
        public boolean initialized = false;
        public int speciesNumber;
        public Map<Integer, ObservableList<Chart>> speciesChartMap = new HashMap<>();
        public Map<Integer, List<ChartPane>> speciesContainerMap = new HashMap<>();
        public List<? extends ChartHelper> helpers;
    }
 
    private final Map<Tab, TabInfo> tabInfoMap = new HashMap<>();
 
[...]
 
        // Inialize helpers for each chart in display.
        final List<Tab> tabList = new LinkedList<>();
        if (BiomassHelper.canPlot(plotRep)) {
            final Tab biomassTab = new Tab(MOViT.I18N.getString("BIOMASS_CHART_LABEL")); // NOI18N.
            final TabInfo biomassTabInfo = new TabInfo();
            biomassTabInfo.helpers = BiomassHelper.createHelpers(plotRepHelperParameter);
            tabList.add(biomassTab);
            tabInfoMap.put(biomassTab, biomassTabInfo);
        }
        if (ExploitableBiomassHelper.canPlot(plotRep)) {
            final Tab exploitableBiomassTab = new Tab(MOViT.I18N.getString("EXPLOITABLE_POP_BIOMASS_CHART_LABEL")); // NOI18N.
            final TabInfo exploitableBiomassTabInfo = new TabInfo();
            exploitableBiomassTabInfo.helpers = ExploitableBiomassHelper.createHelpers(plotRepHelperParameter);
            tabList.add(exploitableBiomassTab);
            tabInfoMap.put(exploitableBiomassTab, exploitableBiomassTabInfo);
        }
        if (MSYHelper.canPlot(plotRep)) {
            final Tab msyTab = new Tab(MOViT.I18N.getString("MSY_CHART_LABEL")); // NOI18N.
            final TabInfo msyTabInfo = new TabInfo();
            msyTabInfo.helpers = MSYHelper.createHelpers(plotRepHelperParameter);
            tabList.add(msyTab);
            tabInfoMap.put(msyTab, msyTabInfo);
        }
        if (YieldHelper.canPlot(plotRep)) {
            final Tab yieldTab = new Tab(MOViT.I18N.getString("YIELD_CHART_LABEL")); // NOI18N.
            final TabInfo yieldTabInfo = new TabInfo();
            yieldTabInfo.helpers = YieldHelper.createHelpers(plotRepHelperParameter);
            tabList.add(yieldTab);
            tabInfoMap.put(yieldTab, yieldTabInfo);
        }
        if (RecruitmentHelper.canPlot(plotRep)) {
            final Tab recruitmentTab = new Tab(MOViT.I18N.getString("RECRUITMENT_CHART_LABEL")); // NOI18N.
            final TabInfo recruitmentTabInfo = new TabInfo();
            recruitmentTabInfo.helpers = RecruitmentHelper.createHelpers(plotRepHelperParameter);
            tabList.add(recruitmentTab);
            tabInfoMap.put(recruitmentTab, recruitmentTabInfo);
        }
        // Catch.rep
        final Tab catchTab = new Tab(MOViT.I18N.getString("CATCH_CHART_LABEL")); // NOI18N.
        final TabInfo catchTabInfo = new TabInfo();
        catchTabInfo.helpers = Arrays.asList(new org.spc.ofp.project.mfcl.chart.helper.catchrep.CatchHelper(catchRep, plotRep, fisheryNames, timePeriodDates, projectionInfo));
        tabList.add(catchTab);
        tabInfoMap.put(catchTab, catchTabInfo);
        // Frq
        final Tab effortTab = new Tab(MOViT.I18N.getString("EFFORT_CHART_LABEL")); // NOI18N.
        final TabInfo effortTabInfo = new TabInfo();
        effortTabInfo.helpers = Arrays.asList(new EffortHelper1(frq, plotRep, fisheryNames, fisheryEffortMap, projectionInfo));
        tabList.add(effortTab);
        tabInfoMap.put(effortTab, effortTabInfo);
        /////////////////////////////////////////////////////////////
 
[...]
 
        tabPane.getTabs().setAll(tabList);
Celui du test qui affiche le problème qui est exécuté juste après :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
System.out.println(tabInfoMap.size());
        System.out.println("----");
        tabList.forEach(tab -> System.out.printf("%s \"%s\" %d %s", tab, tab.getText(), tab.hashCode(), tabInfoMap.get(tab)).println());
        System.out.println("----");
        tabPane.getTabs().forEach(tab -> System.out.printf("%s \"%s\" %d %s", tab, tab.getText(), tab.hashCode(), tabInfoMap.get(tab)).println());
        System.out.println("----");
        System.out.printf("%s \"%s\" %d %s", effortTab, effortTab.getText(), effortTab.hashCode(), tabInfoMap.get(effortTab)).println();
        System.out.printf("%s \"%s\" %d %s", tabList.get(tabList.size() - 1), tabList.get(tabList.size() - 1).getText(), tabList.get(tabList.size() - 1).hashCode(), tabInfoMap.get(tabList.get(tabList.size() - 1))).println();
        System.out.println("----");
        tabInfoMap.entrySet().forEach(entry -> {
            final Tab tab = entry.getKey();
            final TabInfo tabInfo = entry.getValue();
            System.out.printf("%s \"%s\" %d %s", tab, tab.getText(), tab.hashCode(), tabInfo).println();
            System.out.printf("%b %b %b", tabList.contains(tab), tabPane.getTabs().contains(tab), tabInfoMap.containsKey(tab)).println();
        });
        System.out.println("----");
Et on obtient ceci :

Code text : Sélectionner tout - Visualiser dans une fenêtre à part
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
7
----
javafx.scene.control.Tab@38f726a7 "Biomass" 955721383 null
javafx.scene.control.Tab@896f3700 "Exploitable Pop/Biomass" -1989200128 null
javafx.scene.control.Tab@b246acc7 "MSY" -1303991097 null
javafx.scene.control.Tab@2733aae0 "Yield" 657697504 null
javafx.scene.control.Tab@6f7c2fe9 "Recruitment" 1870409705 null
javafx.scene.control.Tab@895e030 "Catch" 144039984 null
javafx.scene.control.Tab@8142d454 "Effort" -2126326700 null
----
javafx.scene.control.Tab@38f726a7 "Biomass" 955721383 null
javafx.scene.control.Tab@896f3700 "Exploitable Pop/Biomass" -1989200128 null
javafx.scene.control.Tab@b246acc7 "MSY" -1303991097 null
javafx.scene.control.Tab@2733aae0 "Yield" 657697504 null
javafx.scene.control.Tab@6f7c2fe9 "Recruitment" 1870409705 null
javafx.scene.control.Tab@895e030 "Catch" 144039984 null
javafx.scene.control.Tab@8142d454 "Effort" -2126326700 null
----
javafx.scene.control.Tab@8142d454 "Effort" -2126326700 null
javafx.scene.control.Tab@8142d454 "Effort" -2126326700 null
----
javafx.scene.control.Tab@38f726a7 "Biomass" 955721383 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@186e6258
true true false
javafx.scene.control.Tab@895e030 "Catch" 144039984 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@2fff10a4
true true false
javafx.scene.control.Tab@896f3700 "Exploitable Pop/Biomass" -1989200128 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@141dde4b
true true false
javafx.scene.control.Tab@2733aae0 "Yield" 657697504 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@65ab8078
true true false
javafx.scene.control.Tab@8142d454 "Effort" -2126326700 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@ebc4237
true true false
javafx.scene.control.Tab@b246acc7 "MSY" -1303991097 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@59911974
true true false
javafx.scene.control.Tab@6f7c2fe9 "Recruitment" 1870409705 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@23846b8b
true true false
----

Pour info, voici les résultats avec le JDK8_11 (tout fonctionne correctement) :

Code text : Sélectionner tout - Visualiser dans une fenêtre à part
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
7
----
javafx.scene.control.Tab@9df514f "Biomass" 165630287 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@3b5fcec0
javafx.scene.control.Tab@234632ea "Exploitable Pop/Biomass" 591803114 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@70f89e4b
javafx.scene.control.Tab@750aeb37 "MSY" 1963649847 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@7b81cecf
javafx.scene.control.Tab@bb2627f "Yield" 196239999 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@38d6a992
javafx.scene.control.Tab@4205b1ad "Recruitment" 1107669421 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@7dba4986
javafx.scene.control.Tab@6d9a664 "Catch" 114927204 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@49048abc
javafx.scene.control.Tab@58f273cb "Effort" 1492284363 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@3c06d2e5
----
javafx.scene.control.Tab@9df514f "Biomass" 165630287 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@3b5fcec0
javafx.scene.control.Tab@234632ea "Exploitable Pop/Biomass" 591803114 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@70f89e4b
javafx.scene.control.Tab@750aeb37 "MSY" 1963649847 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@7b81cecf
javafx.scene.control.Tab@bb2627f "Yield" 196239999 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@38d6a992
javafx.scene.control.Tab@4205b1ad "Recruitment" 1107669421 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@7dba4986
javafx.scene.control.Tab@6d9a664 "Catch" 114927204 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@49048abc
javafx.scene.control.Tab@58f273cb "Effort" 1492284363 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@3c06d2e5
----
javafx.scene.control.Tab@58f273cb "Effort" 1492284363 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@3c06d2e5
javafx.scene.control.Tab@58f273cb "Effort" 1492284363 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@3c06d2e5
----
javafx.scene.control.Tab@9df514f "Biomass" 165630287 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@3b5fcec0
true true true
javafx.scene.control.Tab@4205b1ad "Recruitment" 1107669421 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@7dba4986
true true true
javafx.scene.control.Tab@58f273cb "Effort" 1492284363 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@3c06d2e5
true true true
javafx.scene.control.Tab@234632ea "Exploitable Pop/Biomass" 591803114 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@70f89e4b
true true true
javafx.scene.control.Tab@750aeb37 "MSY" 1963649847 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@7b81cecf
true true true
javafx.scene.control.Tab@bb2627f "Yield" 196239999 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@38d6a992
true true true
javafx.scene.control.Tab@6d9a664 "Catch" 114927204 org.spc.ofp.project.movit.scene.control.review.MFCLReviewPane$TabInfo@49048abc
true true true
----

Je vais tenter contourner le probleme en attachant directemnt le TabInfo dans les proprietes du Tab (une map fourre-tout qui permet de stocker des objets de configuration pour n'importe quel noeud graphique du SceneGraphe) mais j'avoue que ce problème me déconcerte fortement compte tenu de l'usage régulier que je fais des Map...

Ce problème a été posté sur le Jira de JavaFX (en anglais, enregistrement nécessaire) : https://javafx-jira.kenai.com/browse/RT-38382
Ce problème a été posté sur OTN (en anglais) : https://community.oracle.com/message/12596633