Win a copy of Spring in Action (5th edition) this week in the Spring forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Bear Bibeault
  • Devaka Cooray
  • Liutauras Vilda
  • Jeanne Boyarsky
Sheriffs:
  • Knute Snortum
  • Junilu Lacar
  • paul wheaton
Saloon Keepers:
  • Ganesh Patekar
  • Frits Walraven
  • Tim Moores
  • Ron McLeod
  • Carey Brown
Bartenders:
  • Stephan van Hulst
  • salvin francis
  • Tim Holloway

Converting from List To Map?  RSS feed

 
Ranch Hand
Posts: 1605
13
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello,

I have an ArrayList with a 170K Word objects with three fields:

1. Word
2. WordType
3. Definition

What I want to do with this list now is create a Map for efficient searching.

Using the pre-Java 8 way, I can do that like this:


I get the word from the word object for the key and store the item object (the three fields) as the value.

Yet, I can't see how to do this with Java 8 syntax.

Tried to do something similar like this:



But that didn't work.

Suggestions appreciated.

Thanks,

-- mike
 
Bartender
Posts: 9493
184
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
For the second argument, use either word -> word or Function.identity().

Note that this collector will throw an exception if it encounters two Words with the same value. If that is a real possibility you need to supply it with a merge function as well.
 
Master Rancher
Posts: 3001
105
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
hi Mike,

for the toMap you need two mappers, one that maps the element to a key, and one that maps the element to a value (and if there is a possibility of having non unique keys, a BiFunction that tells what to do with the values in that case). You already have the key mapper, you need a value mapper. So the second argument would be: word -> word, or somewhat more stylish: Function.identity().

In that last case: why does the method reference: Function::identity not work?
 
Mike London
Ranch Hand
Posts: 1605
13
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Great info, thanks!

- mike
 
Mike London
Ranch Hand
Posts: 1605
13
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Piet Souris wrote:hi Mike,

for the toMap you need two mappers, one that maps the element to a key, and one that maps the element to a value (and if there is a possibility of having non unique keys, a BiFunction that tells what to do with the values in that case). You already have the key mapper, you need a value mapper. So the second argument would be: word -> word, or somewhat more stylish: Function.identity().

In that last case: why does the method reference: Function::identity not work?



Here's a sample program I wrote to demonstrate where I'm stuck. Searched and searched, but can't find any examples that seem right.


Need a merging function of some type but I can't get the syntax correct.

Tried different variations of "merge" but the syntax is so messy, it's difficult to get right unless you already know how to do it.

Thanks in advance,

- mike
 
Piet Souris
Master Rancher
Posts: 3001
105
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Given that you must deal with synonyms, a Map<String, List<Word>> seems more reasonable.

With the toMap, it means that when we have two equal Strings, we need to put the two associated Words into a List. Now, that is not so easy to accomplish with the toMap method, but the alternative mappng method is in this case very much easier. I'm talking about the Collectors.groupingBy method. See the API of the Collectors class; we can use the simplest version, only specifying the key. So we get:
 
Mike London
Ranch Hand
Posts: 1605
13
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Piet Souris wrote:Given that you must deal with synonyms, a Map<String, List<Word>> seems more reasonable.

With the toMap, it means that when we have two equal Strings, we need to put the two associated Words into a List. Now, that is not so easy to accomplish with the toMap method, but the alternative mappng method is in this case very much easier. I'm talking about the Collectors.groupingBy method. See the API of the Collectors class; we can use the simplest version, only specifying the key. So we get:



Unfortunately, your suggestion above throws a NullPointerException -- "NullPointerException: element cannot be mapped to a null key"

After more searching an experimentation, the code below seems to work. Not sure why this is so (needs to be so) complicated or really any better than the older simple loop that works easily. I get the "flexibility" part, but when the code syntax gets this messy and complicated to do something conceptually simple, something isn't right.

Thanks.

- mike

   
 
Stephan van Hulst
Bartender
Posts: 9493
184
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I don't see why you would need a merging function, as your example doesn't contain homonyms (different definitions using the same words).

If you do have homonyms, you must first decide what you want to do when you encounter a homonym. Piet has already shown what to do in the case you want to group homonyms together.

Some other comments:

Make your objects immutable. Provide all data in the constructor and then don't allow the objects to change afterwards.
Get rid of useless assignments. There's no point in initializing map with an empty HashMap if you're going to overwrite it directly afterwards.
Don't use the same name for a type and a member of that type. If you have a class called Word, the member should be called something like value() or text(), not word().

Even better, I would call the class Definition because you can have multiple definitions that use the same word. Then rename the definition() member to description().

(Definitions from Google.)
 
Stephan van Hulst
Bartender
Posts: 9493
184
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Piet Souris wrote:In that last case: why does the method reference: Function::identity not work?


Function.identity() takes no arguments and returns a Function<T,T>, so Function::identity is a Supplier<Function<T,T>>, not a Function<T,T>.

In other words, Function.identity() supplies the identity function, it's not the identity function itself.
 
Piet Souris
Master Rancher
Posts: 3001
105
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
@Stephan
much more advanced than I had in mind, but what a beautiful example!

Question: why do you use a Word factory 'valueOf', instead of a good old fashioned constructor? Is that the current trend perhaps?

@Mike
Did you, by any means, use your Test class, that you showed some posts ago? There you create three Words: one, two and three. Have a careful look at your code: are all of the three words neatly initialized? Or might there be one or two that have nulls as field values?
 
Piet Souris
Master Rancher
Posts: 3001
105
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Stephan van Hulst wrote:Function.identity() takes no arguments and returns a Function<T,T>, so Function::identity is a Supplier<Function<T,T>>, not a Function<T,T>.

In other words, Function.identity() supplies the identity function, it's not the identity function itself.


Indeed. For another example: see the Sybex OCPJP book by Jeanne and Scott. Chapter 4, page 199 above.

I don't know if this confusing matter could be asked on the exam, but any way: worth taking a very good look!
 
Stephan van Hulst
Bartender
Posts: 9493
184
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Piet Souris wrote:why do you use a Word factory 'valueOf', instead of a good old fashioned constructor? Is that the current trend perhaps?


I add a factory method when the type is a value type (equality of two instances is determined by all of their fields). The factory method is free to decide whether it returns a new instance or a cached instance. Even if it creates a new instance every time in the current version of the code, it might not in the future. By making the constructor private, clients are forced to call the factory method, and when you decide to optimize the code by caching instances that are created very often, clients benefit for free.

Imagine you had an entire book in memory, with every word represented by an instance of the Word class. Creating a new object for every word in the book might take up a lot of memory, (especially for words like the indefinite article "a"), while referencing cached instances is a lot cheaper.
 
Stephan van Hulst
Bartender
Posts: 9493
184
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
A second case is when the total number of possible values is small. You can then use the valueOf() method to guarantee that equal instances will always be identical, and replace calls to the equals method with calls to the identity comparison operator:

Note that I didn't have to override the equals() and hashCode() methods, because the definitions given by Object are already correct.
 
Sheriff
Posts: 21464
97
Chrome Eclipse IDE Java Spring Ubuntu VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
(Except in a multi-threaded environment, because you then have a race condition )
 
Stephan van Hulst
Bartender
Posts: 9493
184
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Good point. That would merit a fix.
 
Piet Souris
Master Rancher
Posts: 3001
105
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
At the risk of hijacking this topic: why would you limit the number of playingcards? That seems more like the job of some GameDeck. And two cards are never equal, although they might have the same value, game dependent.
 
Stephan van Hulst
Bartender
Posts: 9493
184
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You would limit them IF two cards with the same rank and suit are considered the same. In this case, two players holding cards that have the same rank and suit is represented by two hands referencing the same card. It's true that you can now no longer distinguish between two "copies" of the same card using the reference equality operator, but as far as I know no card games exist that distinguish between one "seven of hearts" and another "seven of hearts". It's usually very suspicious when you have to depend on the reference equality operator because you need to do identity comparisons rather than value comparisons.

You would only do this for types that have a small and fixed number of possible values though, for instance, when all their fields are enums or are otherwise very limited in the values they can hold.
 
Stephan van Hulst
Bartender
Posts: 9493
184
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Here's a pile of cards containing two decks:

Even though all equal instances are also identical, the pile still contains 104 cards (references to PlayingCard instances).
 
Marshal
Posts: 6257
420
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mike London,

Cowgratulations, your topic has been picked to be published in CodeRanch's August journal.
 
Consider Paul's rocket mass heater.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!