I was wanting to take values from a HashMap into a list and then sort the list, and I noticed online a suggestion it's better to create your own class which extends AbstractMap.SimpleEntry for doing this.
Basically I wanted it to support <String, Integer> and <Character, Integer> and be sortable by the numerical value field, but if it could support more types that's a bonus, like Character, Double.
I'm not sure how to do the declaration the right way though.
are any of these close to being correct?
I know that T parameters are meant to restrict the type, but I'm not really sure how to use them. oracle site is not much help.
Also, it won't let me use wildcards on the params in class definition, it says not allowed in the superconstructor.
Programming is not about guessing, but about reasoning. First you need to think about what abstract concept you want to represent, and how to represent it. You're jumping ahead of thing by extending and implementing types that you don't know you need.
It appears that you want to express a quantity of some thing. "Quantity of" implies a generic type, because generic types are generally read aloud as "Something of something else". So let's start with:
Now, what else do you want to do? It appears that you want the amount to be represented by other types than just integers. So lets make the amount generic as well. The amount can be any type, as long as it's a Number:
It also appears that you want to compare two quantities by amount. Making it a Comparator is no good, because comparators are used to compare other types. Instead, normally you would do this by making a class Comparable, but in this case that's also not a good solution, because how are you going to compare a quantity of apples to a quantity of oranges? It *is* possible to define a partial ordering on a type using Comparable, but you need to document the behavior really really well, because types with a partial ordering have the very confusing property that the compareTo() method is not consistent with equals(). An example of a confusing side effect: Adding a quantity of 5 apples to a TreeSet and then adding a quantity of 5 oranges to the same set will result in the set only containing the quantity of 5 apples.
The best solution here is to create utility methods that return a Comparator that can be used to compare two quantities by amount:
Now, imagine you have a Map<Apple, Float>. You can then create a list of sorted quantities like this:
Finally, lets add the equals(), hashCode() and toString() methods for good form:
As you can see, there is no reason for Quantity to implement Map.Entry or Comparator.
I understand most of your code even though I can't code that good myself. That's god-level programming!
I hate to admit it but I have just barely started using implements and extends, because it wasn't covered in the java class I took. So I'm left to learn lots of stuff on my own now. I wonder what the real difference is between implementing an interface such as Map.Entry<K,V> and extending a class which implements it such as AbstractMap.SimpleEntry<K,V> or doing it your way and making a new class from scratch entirely.
Main problem for me, I don't understand how to make the for-loop work with a custom class like this. I need to dump hashmap entrySet's from a for-loop into the Quantity's and then list and sort those.
What I'm using this for is that I'm making a substitution cipher cracker. I have a String message, and I generated 2 hashmaps. one is the observed character count, the other is the expected char count based upon a language frequency table. So basically you need to dump the entries from the maps into key/val lists, then sort the lists by highest to lowest val count, and then create a new hashmap for letter re-assignments <R,A>, <E,B>, etc. Now you can decode the message by swapping letters. A more difficult cipher can be with strings. So I would need String,Integer for that. Also the frequency tables use Char,double or String,double.
i was going to do the dumping out and sorting like this:
this part of your constructor confuses me. looks like getter methods in the constructor???
Stephan van Hulst
posted 5 months ago
Those are not part of the constructor. They ARE getter methods. I'm not a big fan of prefixing getter methods with 'get-' if they're just simple properties of the object.
Anyway, the part that you want to do with the for-loop I've done like this:
If you want to do it with a for-loop rather than with streams, it would look like this:
You would implement an interface like Map.Entry if you need to use your class in a context where Map.Entry is needed. For instance, if you wanted to create your own Map implementation that kept track of quantities, Quantity would implement Map.Entry (probably by extending AbstractMap.SimpleImmutableEntry). However, you never use Quantity as an entry in a map, so it's not necessary to extend or implement anything Entry-related.
Note though that Quantity almost does exactly the same thing as AbstractMap.SimpleImmutableEntry, except that it restricts the value type to a Number. You can achieve the same thing by just using Map.Entry, and writing your comparators accordingly:
This completely eliminates the need to have a custom Quantity class if you think it's too much.
I think a Quantity class is a bit much., though it makes fr a very nice example!
In your first post, what would the 'T' in' Quantity<T>' represent? You mention a Quantity<Apple, Float>, but wouldn't it make sense to have T represent a Unit of some kind, for instance 'kilo', percentage, intrinsic et cetera?
Stephan van Hulst
posted 5 months ago
Sure, and 'unit' would have been a better name than 'instance', but I got up early this morning.
its a great example also for the modern style of code, I don't see this kind of coding-style in website examples or my books.
I'm really not sure how the comparator works when it's broken into 3 seperate statements like that and without if-statements, I haven't seen it done that way before.
for comparator with generics here is what they are showing on tutorialspoint:
do the streams and lambdas make the code run faster or why is everyone starting to use these?
does stream completely replace the for-loop of java or is it just for managing collections?
I tried using streams before and couldn't get them to work at all because I didn't know all the weird pointer lambda syntax and commands for it.
Stephan van Hulst
posted 5 months ago
I don't think that code is comparable (hehehe). Your example returns the greatest of three objects. That's very different from implementing Comparable or Comparator. The purpose of these methods is completely different.
An old way of implementing a Comparator would have been thus:
You'll agree that the 'modern' code I wrote is much more declarative and succinct.
People use streams these days because streams are declarative, while for-loops are procedural. Declarative code says what you want to achieve, procedural code says how you want to achieve it.
In the example I used in a previous post, the version that uses streams essentially says "I have a map of occurrence counts of characters, and I want to transform it to a sorted list of quantities". It depends on how the Java designers implemented streams how this transformation actually happens. Do we really care?
The version that uses a for-loop literally says "First create a list, then create a new Quantity for each entry in the map, and finally sort the list". First of all, this solution doesn't allow Java to decide to use multiple threads to speed up the process, we just literally told the program to create and add each quantity one by one. We also explicitly told the program to sort the list afterwards. The solution that uses streams may sort all the elements when it's done adding them, but it's also free to add everything to a data structure that keeps itself sorted at all times. Again, we don't care how it does it, we just care that the end result is sorted.
Using streams doesn't necessarily make the code faster. Actually, they probably make the code a little bit slower, but you won't really notice it. It's just less likely that you have to update your code in the future, because much of the responsibility is moved from your code to the implementation of streams.
The new style is great but I can't always figure it out. Like right now I can't figure out how to use stream to get Quantity.instance out of the 2 sorted lists and put them into a new map. Not sure it's even possible, or if support has to be added somehow for it to work?
also was .collect(toList()); in the example above supposed to be .collect(Collectors.toList()); ?
here's what I got so far:
Map chReplacementsEnglish = observedSorted.stream().collect(Collectors.toMap(Quantity::instance, Quantity::instance));
It doesn't stream both the lists into the same map at the same time, but I'm starting to understand how to use stream.
Stephan van Hulst
posted 5 months ago
You're saying you want to stream both lists into a map, but you haven't explained what you expect the result to be. What does it represent, and given two example lists, what does the result look like? What does it represent?
using those freq tables, i can compute an approximate guess how many of each letter are in a message of any given length.
for example if the message is 20 chars long, i might expect 5 e's, 3 a's, but no n's at all in english, but those expectations will differ for dutch, and there is more chance of the n so maybe we expect 2 n's.
(i didn't really compute those numbers, so its just an example.)
you will not know what the language is beforehand, since its all just scrambled letters, so you must decode for each possible language trial and error style.
i was just finding the most common letter in the message, then re-mapping it to the letter it was most likely to be in the chosen language.
the hashmap i was creating is for replacing. fakeLetter -> realLetter
i assumed that replacing by frequency alone would be perfect, but i was wrong, it gives such a poor result that it never creates something readable.
(i didnt ever figure out how to make the hashmap using the streams, i tested it a different way.)
so my entire algorithm is a failure. my fault, i didn't look up the correct algorithm, i tried to design it myself.
i have asked here before and nobody at java ranch knows anything about crypto or any things related to it such as hill climbing & chi square tests.
so i am left to figure a lot out on my own, very few websites deal with this, and the practical crypto site which is the best one to consult has no forum at all.
i would have to re-tool my app now for quadgrams, i don't know if i can even find tables for those, if not i would have to gather lots of texts just to analyze and build quadgram freq tables myself.
accurate calculation of this would need tons of plain texts in various languages, 1000 pages of text or more per language i think, not sure. finding all this text would be hard.
where can you download entire books of ascii texts in all languages?
but after all that i would still have to figure out a hill climbing algorithm. so this isn't a very beginner type of project. this is why i just ask how to do certain things and not give much details about what i'm trying to do.
because you all will think i'm crazy for trying it!
and on top of that you won't understand it, which is funny since i had thought everyone here was taught to learn crypto breaking stuff like this at uni, i'm not in uni i'm just teaching myself.
i have spent a year creating this thing so far, it has charts and graphs to show observed and expected counts since i coded the ui in javaFX. i learned a lot doing it even if i can't finish it.
i would be very sad if i can't finish it though since i put so much work into it.
All of the world's problems can be solved in a garden - Geoff Lawton. Tiny ad:
RavenDB is an Open Source NoSQL Database that’s fully transactional (ACID) across your database