3

I have been trying to figure out why this is no longer working. It was all working fine, nothing changed with the object or the function that calls it. I am using Lombok for the getters, setters, and originally the toString but I don't understand how it would hijack a getter. I have also written explicit getters to troubleshoot to no avail.

Class

@Data
class Engineer {
    private String name;
    private String tech;

    @Override
    public String toString() {
        return "test " + name;
    }
}

Method using it

public void example(List<Engineer> engineers) {
    //creates keys with toString and creates duplicates
    Map<String, Engineer> streamMap = engineers.stream().collect(Collectors.toMap(Engineer::getName, Function.identity()));

    //while this one works
    Map<String, Engineer> forLoopMap = new HashMap<>();

    for(Engineer engineer : engineers) {
        forLoopMap.put(engineer.getName, engineer);
    }
}

I troubleshot this by making a custom toString with just a string and the name. My original error was a lazy loading issue because toString was trying to get those objects.

After I made the custom toString it started giving me duplicate key errors when I know the names remain unique.

This is not only happening for one list of data but a separate entity list as well using a different unique identifier.

I have been using this stream pattern for awhile and it was working fine but now it is having issues. Any thoughts would be appreciated.

EDIT - TESTS:

  • Explicitly made toString (calling getName as a method reference calls toString)
  • Explicitly made getName (originally generated by Lombok plugin)
  • Works in dev profile but not with prod profile - must be something in the difference
  • Is this your real code? – Mạnh Quyết Nguyễn Jun 13 '18 at 17:25
  • @RishikeshDhokare What difference would it make? – Thiyagu Jun 13 '18 at 17:28
  • There seems to be nothing wrong with your code. Problem must be somewhere else – Thiyagu Jun 13 '18 at 17:30
  • Where is `getName` defined? Your for loop seemingly has `engineer.getName`, which isn't a field, it should be a function, if you use the functional reference above. – ifly6 Jun 13 '18 at 17:47
  • Lombok defines the getName but I did troubleshoot it by explicitly defining the getName function as well – ofgodsandmythos Jun 13 '18 at 18:02
  • 1
    My actual code grabs entities from two different services, stream maps them to their similar field as the key (name/crmId) and merges the two separate sources. This was the slimmed down version and where it actually breaks - the engineer has a bunch of fields that are irrelevant – ofgodsandmythos Jun 13 '18 at 18:07
  • 1
    That rings a bell. I vaguely remember something about a byte code transformation tool messing up [`BootstrapMethods` attributes](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.23) of a class, resulting in method references becoming associated with wrong target methods. – Holger Jun 14 '18 at 11:55
  • 1
    [That was it](https://stackoverflow.com/q/29146567/2711488). Does not seem to be directly related. However, both, ProGuard and Lombok are byte code transformation tools… – Holger Jun 14 '18 at 12:16

1 Answers1

0

You can provide mergeFunction which is BinaryOpearator for duplicates key. In case of duplicate key the last value will be overwritten.

  Map<String, Engineer> streamMap = engineers.stream()
                .collect(Collectors.toMap(Engineer::getName, Function.identity(),
                         (e1, e2) -> {
                            System.out.println("Duplicate key found!"); 
                            return e2;
                         }
                        )
                );
Gaurav Srivastav
  • 2,381
  • 1
  • 15
  • 18
  • This is probably the best way to handle the duplication. I just reverted to the definite for loop since getName is still calling to string within the collector. – ofgodsandmythos Jun 13 '18 at 18:03
  • 2
    This solution is hiding a bug rather than identifying the root cause and fixing it. This most likely will lead to other sorts of unexpected issues. – Ousmane D. Jun 13 '18 at 18:09
  • I realized now I misspoke. That would handle duplicate values but not duplicate keys – ofgodsandmythos Jun 13 '18 at 18:24
  • @ofgodsandmythos The merge function is used to resolve collisions between values associated with the same key. in other words, it handles duplicate keys. it's perfectly permissible for a map to have duplicate values as they're not required to be unique. – Ousmane D. Jun 13 '18 at 18:42
  • Ah yes. I revisited the docs and confused myself. Thanks for the clarification – ofgodsandmythos Jun 13 '18 at 19:02