For some reason, I'm drawing a blank on this one. I have an ArrayList that contains CDs (some of them identical in name) and I want to print a String that tells how many I have of each CD. For example, a string that says "You have: (1) AlbumOne, (2) AlbumTwo, (1) AlbumThree" etc. The CDs are not sorted. How can I do this?
One way to do it is to loop thru the array, and use a map that uses the objects as keys and the values as counts. So
Map<YourObject, Integer> counter ...
As you loop thru the array, do a get on the counter for the current object in the array. If you get null, initialize the value at that bucket in the map to be 1. If you have a value, increment it. Then you can loop over the map to get your readout.
Note that if you use a Hashmap, your objects have to implement the hashcode and equals method properly. You don't have to use your object, if it has keys or some other distinguishing field, the keys in your map can be that...
//aggregate details
Map<String, Integer> albumCounts = new HashMap<String, Integer>();
for (String album : albums) {
Integer count = albumCounts.get(album);
if (count == null) {
count = 0;
}
albumCounts.put(album, count + 1);
}
//print stats
System.out.println("You have:");
for (String album : albums) {
System.out.println("(" + albumCounts.get(album) + ") " + album);
}
Don't get confused about the Map. Use of Map is appropriate to solve such a problem (as you have posted) So please visit this link (tutorial) and read/learn about Map.
Related
So I got a treemap to which I add courses that come from a random generator, the names are consistent but the order is not. For example I got these:
List course = new ArrayList();
course.add("COP2210");
course.add("COP2250");
course.add("ENC1250");
course.add("MLP1337");
course.add("ENC3250");
course.add("REL2210");
course.add("MUS3200");
course.add("PHI1240");
course.add("SOL5000");
course.add("HAL9000");
Now that's ten courses in total, and while the code below works to count one of them it would mean i have to get about nine more int variables written and repeat the if statement another nine times, that would be undesirable. I should mention the treemap is inside a loop and resets everytime, which is something I want. but a second treemap can be made outside the loop to add the courses, i got no issue with that. But it is entirely possible that some courses never even gets added to the treemap as they are randomly chosen from the ten above.
for (int i = 0; i < NumberOfStudents; ++i){
order = RandomCourse();
TreeMap<String, Integer> tmap = new TreeMap<String, Integer>();
tmap.put(courses.get(order[0]).toString(), 0);
tmap.put(courses.get(order[1]).toString(), 0);
tmap.put(courses.get(order[2]).toString(), 0);
String comparator1 = courses.get(order[0]).toString();
String comparator2 = courses.get(order[1]).toString();
String comparator3 = courses.get(order[2]).toString();
if(comparator1 == "HAL9000" || comparator2 == "HAL9000" || comparator3 == "HAL9000")
HAL9000students++;
courses.indexOf(order[0]);
//Clean variable
SortedCourses = "";
//Map logic
Set set = tmap.entrySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()) {
Map.Entry mentry = (Map.Entry)iterator.next();
SortedCourses = SortedCourses + mentry.getKey() + " ";
}
students.add(ID_Prefix + i+ ", " + student.get(i) + " " + SortedCourses);
}
Order is a int array of size 3. Where a random number is stored, it is then used to choose from courses list a position which corresponds to a course.
Whenever you have a situation where you need to do the same sort of operation for a lot of different values, it should give you a hint not to use individual variables, but rather an array or a collection.
You want to get the number of students in each course. So instead of keeping a variable for every course (what happens if there are 15 courses? 100?), you can use a map from the course name to the number of students in that course.
In this case, since you are already dealing with the indexes of the courses rather than their name, you can actually do that with an array.
int[] studentCounts = new int[course.size()];
This will be, of course, declared outside the loop. In this array, studentCounts[0] will be the number of students in the course indexed 0 ("COP2210"). studentCounts[1] will be the number of students in course index 1 ("COP2250") and so on.
So, if your order array is, for example, {3,5,9}, then you'll need to increment studentCounts[3], studentCount[5] and studentCount[9]:
for ( int courseIndex : order ) {
studentCounts[courseIndex]++;
}
Your second issue is that you want to print the names of the courses selected for each student ordered alphabetically. You use a TreeMap for that, but there is no reason to use a map - you are not actually using the values of the map, just the keys. So a TreeSet is going to make more sense.
So if you declare
TreeSet<String> sortedCourseNames = new TreeSet<>();
You can add to it inside the loop above:
for ( int courseIndex : order ) {
studentCounts[courseIndex]++;
sortedCourseNames.add( course.get(courseIndex) );
}
A Set has a simpler iterator than a Map. You can get the strings directly:
StringBuilder sb = new StringBuilder();
sb.append(ID_PREFIX)
.append(i)
.append(',')
.append(student.get(i))
.append(' ');
for ( String courseName : sortedCourseNames ) {
sb.append(courseName)
.append(' ');
}
students.add( sb.toString() );
So, some notes:
Java has coding conventions. Type names (class,interface,enum) should start with a capital letter (e.g. BigPicture). Method, variable and field names should start with a lowercase letter (e.g. bigPicture), and constants should be all-caps (e.g. BIG_PICTURE). So a variable name like SortedCourses is bad, and so is HAL9000students and ID_prefix. I assumed that the ID prefix is a constant (declared static final String), so ID_PREFIX is the correct name. But if it's a variable, it should be idPrefix.
Do not use raw types. Never declare anything to be a List, TreeMap, etc., without using the base type. It has to be List<String>, TreeMap<String,Integer>, Set<Map.Entry<String,Integer>> and so on. Using generics properly is important for type safety, and it saves you casting. So don't ignore those warnings about "raw types" that the compiler gives you!
In my solution, you don't need to compare strings. But if you need to compare strings, you do it by str1.equals(str2), not str1 == str2.
Don't use the operator + to concatenate strings in loops. It's good enough for something like a = "foo " + b + "bar" - a single string concatenation. But in a loop:
String result = "";
for (i = 1; i < 5; i++) {
result = result + " something else ";
}
Too many objects are created and discarded. It's better to use a StringBuilder as I have shown.
In my class Feeds I have along with other members a member variable called "Date" which is of String type. I have an ArrayList of Feeds objects. I want to find the occurrences of objects which have the same date String. The occurrences can then be put in a HashMap that contains the String Date as key and # of occurrences as value.
Something along these lines:
List<Feeds> m_feeds = new ArrayList<Feeds>();
//add all feeds objects
m_feeds.add(...);
int occurrences = 0;
HashMap<String, Integer> repeatedDatabase = new HashMap<String, Integer>();
for (Feeds f : m_feeds){
occurrences = Collections.frequency(m_feeds, f.date);
// i know this method compares objects but i want to know how
// only a single variable can be done
repeatedDatabase.put(f.date, occurrences);
}
Other than giving you a simple solution, I took the liberty of fixing some things in your code please take a look:
List<Feeds> mFeeds = new ArrayList<>(); //If you are using Java 7+ you do not need to declare explicitly the Type in Diamonds. If you aren't, ignore this. Also fixed name to adapt to Java standards.
//add all feeds objects
m_feeds.add(...);
HashMap<String, Integer> repeatedDatabase = new HashMap<>(); //See Above.
for (Feeds f : m_feeds){
String s = f.date; //Suggestion: use a getter method, do not make public variables accessible outside class
Integer i = repeatedDatabase.get(s);
if (i == null){
repeatedDatabase.put(s, 1);
} else {
repeatedDatabase.put(s, i+1);
}
}
Your code will work if you properly overrode equals and in the Feeds class to return true for two Feeds instances having the same date (since if you try to put the same key in the Map twice, the new value will override the old value, and since in your case the values would also be the same, it would make no difference). However, each call to Collections.frequency would iterate over the entire List, which would give you an O(n^2) time complexity.
One way to make it more efficient :
for (Feeds f : m_feeds){
if (!repeatedDatabase.containsKey(f.date)) {
occurrences = Collections.frequency(m_feeds, f.date);
repeatedDatabase.put(f.date, occurrences);
}
}
This would still do more iterations than necessary. It would call Collections.frequency once for each unique date, which means you would iterate the List as many times as there are unique dates.
A more efficient implementation will not use Collection.frequency at all. Instead, you'll iterate just one time over the list and count the number of occurrences of each date yourself. This would give you an O(n) time complexity.
for (Feeds f : m_feeds){
if (!repeatedDatabase.containsKey(f.date)) {
repeatedDatabase.put(f.date, 1);
} else {
repeatedDatabase.put(f.date, repeatedDatabase.get(f.date)+1);
}
}
Why don't use directly the hashMap?
you can do something like
HashMap<String,Iteger> map = new HashMap<>();
for (Feeds f : m_feeds){
if (map.contains(f.getDate()) { // use the method to get the date
map.put(f.getDate(),map.get(f)+1);
else
map.put(f.getDate(),1);
}
I didn't test the code but it should work.
A small update to Angelo's answer..pushing it a bit further.. you can also use a map of string,int[] like this
Map<String,int[]> map = new HashMap<>();
int[] countArray = map.get(key);
if(countArray == null)
map.put(key, new int[]{0});
else
countArray[0]++;
Using the beauty of references :)
I'm learning Java using BlueJ, I have made a class that has a HashMap of (Integer, String) that contains an ID number of somebody and their name.
I want a method to return a collection of all the keys that satisfy a condition, like if their ID number begins with 3 for example. I can't figure out how to do this.
And then another method that returns a collection of the values if they satisfy a condition, I was thinking it would be very similar to the previous method.
I know I need to loop through the map but I am not sure how to write the condition to populate the new map.
Here's an example that returns all the odd keys, in a Collection. Lists and Sets are Collections, in the same way that ArrayLists are Lists. You could change Collection to List (or even ArrayList) in this example and it would do the same thing.
public Collection<Integer> getOddKeys() {
// keySet is a method of Map that returns a Set containing all the keys (and no values).
Collection<Integer> result = new ArrayList<Integer>();
for(Integer key : map.keySet()) {
if((key % 2) == 0) // if the key is odd...
result.add(key); // ... then add it to the result
}
return result;
}
You should be able to modify this example to check the values instead - I won't just give you that code, because it's very similar, and easy to figure out if you understand how this example works.
You need to use the values method, which returns a collection of the values, in the same way that keySet returns a collection (more specifically, a set) of the keys. If you're wondering about why keySet returns a set and values doesn't, it's because you can use the same value twice in a map, but you can't use the same key twice.
You could do the following:
Create a holder list
Iterator over your map keys using map.keySet().iterator();
Check if the key start with 3, if yes add it to the key list.
return the keys list.
In your case (if the map is not too big), I'll get all keys of the map, then process them one by one to math my criteria:
Map<Integer, String> myMap=getFromSomeWhere();
for(Integer i : myMap.keySet() {
String k=String.valueOf(i);
if(k.startsWith("3")) {
//do what you want
}
}
public void CountryAbbriviationMap(String input)
{
map<string ,string> countrymap =new map<string ,string>{'Australia'=>'AUS','Argentina'=>'ARG', 'India'=>'IND'};
for(string key : countrymap.keySet())
{
if(key.startsWithIgnoreCase('A') && input.startsWithIgnoreCase('A'))
{
system.debug(key); //TO GET KEYS
system.debug(countrymap.get(key)); //TO GET VALUES
}
}
}
So what I have been trying to do is use a TreeMap I previously had and apply it to this method in which I convert it into a set and have it go through a Map Entry Loop. What I wish to do is invert my previous TreeMap into the opposite (flipped) TreeMap
'When I run my code, it gives me a comparable error. Does this mean I have to implement the comparable method? I convereted the arrayList into an Integer so I thought the comparable method would support it. Or is it just something wrong with my code
Error: Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.lang.Comparable
Overview: Originally, my intended purpose for the program was to make a Treemap that read from a text document and specifically found all the words and the index/rows of where the words were located. Now I wish to make a "top ten" list that contains the most used words. I wanted to "flip" my treemap so that the integer values would be what would be put in order and the string would follow
public static void getTopTenWords(TreeMap<String, ArrayList<Integer>> map) {
Set<Map.Entry<String, ArrayList<Integer>>> set = map.entrySet();
TreeMap<Integer, String> temp = new TreeMap<Integer, String>();
int count = 1;
for(Map.Entry<String, ArrayList<Integer>> entry : set){
if(temp.containsKey(entry.getValue())) {
Integer val = entry.getValue().get(count);
val++;
temp.put(val, entry.getKey());
}
else {
temp.put(entry.getValue().get(count), entry.getKey());
}
count++;
}
}
Now I wish to make a "top ten" list that contains the most used words.
I wanted to "flip" my treemap so that the integer values would be what
would be put in order and the string would follow
Note that a Map contains only unique keys. So, if you try to keep your count as key, then you would need to put it in your Map by creating a new object with new Integer(count).
If you put your count in Map like: - map.put(2, "someword"), then there are chances that your previous count value gets overwritten, because Integer caches the values in range: - [-128 to 127]. So, the integer values between these range will be interned if you don't create a new object. And hence two Integer with value say 2 will point to same Integer object, and hence resulting in duplicate key.
Secondly, in your code: -
if (temp.containsKey(entry.getValue()))
using the above if statement, you are comparing an ArrayList with an Integer value. temp contains key which are integers. And values in entry are ArrayList. So, that will fail at runtime. Also, since your orginal Map contains just the location of the word found in the text file. So, just what you need to do is, get the size of arraylist for each word, and make that a key.
You would need to modify your code a little bit.
public static void getTopTenWords(TreeMap<String, ArrayList<Integer>> map) {
Set<Map.Entry<String, ArrayList<Integer>>> set = map.entrySet();
TreeMap<Integer, String> temp = new TreeMap<Integer, String>();
for(Map.Entry<String, ArrayList<Integer>> entry : set) {
int size = entry.getValue().size();
int word = entry.getKey();
temp.put(new Integer(size), word));
}
}
So, you can see that, I just used the size of the values in your entry set. And put it as a key in your TreeMap. Also using new Integer(size) is very important. It ensures that every integer reference points to a new object. Thus no duplication.
Also, note that, your TreeMap sorts your Integer value in ascending order. Your most frequent words would be somewhere at the end.
I have a List of object as List<Human> humanList;
which human object consist of 5 variable as firstname, lastname,color,length,sex.
How can i check out to see the variable(firstname) how many times appear/exist in the list?
List<Human> humanList = getHumanList();
for(Human human :humanList){
}
Assuming here firstname is of type String, if it is not - change it as needed.
Create a Map<String,Integer> (let it be map) which will be used as a histogram.
Iterate the list, and for each human:
Integer temp = map.get(human.firstName); //search for the current number of occurances
if (temp == null) map.put(human.firstName, 1); //first occurance of this firstname
else map.put(human.firstName,temp+1); //not first occurance, modify the map.
When you are done map holds each firstName as a key and the number of its occurances as the matching value.
You can use guava multiset count method http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained.
Multiset<Human> set = HashMultiset.create();
set.count(Human.firstName);
You need to traverse the list counting occurences O(N).
A better option if you need this often would be to increment a counter as you add/remove from the list and use that instead of traversing