Here's another use-case:
I have an algo that creates Opps and Opportunity Lineitems , then rolls up the Opportunity Lineitems based on part product-code, then inserts Opps, then finally the OLIs.
// helper collection needed for roll-up routine
Map<OpportunityLineItem, String> opportunityLineItemToProductCodeMap = new Map<OpportunityLineItem, String>();
This is implemented with a deferred DML chain (for bulkification), so neither the OLIs nor the Opportunities have Ids until after the roll-up.
When I assign a part (using PricebookEntryId) to the Opportunity Lineitem, I found that I could not reference through to the part's Sku from the Oli object (because it's uncommitted), so I had to maintain a data structure that contains the Oli to product code relationship purely for the roll-up exercise.
Why I Think It's Okay In This Case:
Because 2 identical lineitems would map to the same product code, I don't need to worry about the case where identical field values result in an overwrite of the product code (it would be the same). I also guarantee that once the supporting data structure is in use, the keyed OLIs never change until after the data structure is utilized.
Note that this wouldn't be necessary if my requirement was to roll-up based on part-Id rather than product code. A part-Id based roll-up would require that product code is unique across all parts, and I have duplicates in my data set. Instead, I am able to roll-up on product code only because I can assert that all parts with duplicate product codes are interchangeable (from my stakeholder's perspective) and the price is not prescribed by what might be a variable list price, but rather by the opportunity line item unit price itself, which is being calculated independent of list price.
Alternatively...
However, the above is probably less readable and maintainable than a inner-class holding both the OLi and the product code String, which could then be stored together in a single list.
Similarly, one could use two lists and rely on the ordinality of lists to extract the elements from both while looping through only 1 (the OLI list).