Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8094179

[Tab] Regression caused by bad hashCode in Tab class

XMLWordPrintable

      This issue seems to have been created by RT-36565

      As of JDK8_20, I experience issue where Map<Tab, ?> (HashMap in my case) sometimes fails to detect it contains a Tab value and thus return null for values attached to this tab.

      When going through debugging, the only thing that seems to have been changed in u20 is the inclusion of a new hashCode() method for the Tab class.

      My guess is that the problem comes from this method. As of u20, the hashCode() method is as follows:

      @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;
          }

      In previous versions the Tab class used the hashCode() method from the Object class and everything was working fine.

      Note that I am not able to reproduce this issue in a simple self-contained program. It does however happen in one of my projects.

      Here we use a Map<Tab, TabInfo> instance to store some tab configuration values. As each user switch tab, the content of the tab is populated (tabs are initially empty).

         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);

      Then here is some diagnostic code that is executed immediately after the map has been populated:

      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("----");

      In u20 the output for this code is as follows:

      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


      In u11 the output for this code is as follows (everything is working fine):

      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
      ----

      I will attempt to circumvent the issue by attaching the tabInfo object to the properties of the tab instead of storing it in a separate Map.

      Notwithstanding, the issue is pretty much severe and code breaking !

            jgiles Jonathan Giles
            fbouyajfx Fabrice Bouyé (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported: