The question is how to pritn the books that have value = 1 in the HashMap, using the printWhiteRavens(ArrayList<String> whiteRavens) method ? In the code ive deleted the array with the books so the code could be shorter.
public static HashMap<String, Integer> createBooksCounter() {
HashMap<String,Integer> createBooksCounter = new HashMap<>();
createBooksCounter.put("Chicken Abroad",5);
createBooksCounter.put("Lord With Sins",3);
createBooksCounter.put("Fish And Horses",4);
createBooksCounter.put("Mistress Of Devotion",2);
createBooksCounter.put("Guardians And Gangsters",3);
createBooksCounter.put("Bandit Of The Void",1);
createBooksCounter.put("Lions Of Dread",1);
createBooksCounter.put("Future Of Fire",2);
createBooksCounter.put("Driving Into The Depths",1);
createBooksCounter.put("Starting The Demons",1);
createBooksCounter.put("Maid With Blue Eyes",2);
createBooksCounter.put("Lovers In The Forest",1);
createBooksCounter.put("Destruction Of The Faceless Ones",1);
return null;
}
public static void countBook(HashMap<String, Integer> booksCounter, String book) {
}
public static ArrayList<String> findWhiteRavens(HashMap<String, Integer> booksCounter) {
return null;
}
public static void printWhiteRavens(ArrayList<String> whiteRavens) {
return null;
}
public static void main(String[] args) {
}
Using Stream :
yourMap.entrySet().stream().filter(e -> e.getValue() == 1)
.forEach(element -> System.out.println(element.getKey()));
Your method createBooksCounter should probably return the map instead of null. Note that usually you declare members as the highest common interface offering the contract you need. So even though you might instantiate HashMap, you declare the field as a Map. The same holds for method parameters and return types. Regarding the name, I would name it bookCounts, since the map is not counting anything, just representing books and their 'counts'.
public static Map<String, Integer> createBookCounts() {
Map<String, Integer> bookCounts = new HashMap<>();
bookCounts .put("Chicken Abroad",5);
// ...
return bookCounts;
}
The method findWhiteRavens needs to look up all books in the map which have count==1. This is contrary to the normal usage of a Map, where you look things up by their key rather than their value. I suggest either to use a map where count is the key, and the values are lists of books (having that count) OR to use a List with Book objects with the fields title and count. The latter approach is in my opinion the simplest and most object-oriented design (using maps to collect related fields is a code smell):
class Book {
String title;
int count;
Book(String title, int count) {
this.title = title;
this.count = count;
}
// accessors to be added
}
// ...
List<Book> books = new ArrayList<>();
books.add(new Book("Chicken Abroad", 5));
You could print books with count==1 in one go like this:
books.stream()
.filter(book -> book.getCount() == 1)
.map(book -> book.getTitle());
forEach(book -> System.out.println(book));
If you prefer to use a map, then this would make more sense:
private static Map<Integer, List<String>> bookTitlesByCount = new HashMap<>();
public static void main(String[] args) {
addBookTitle("Chicken Abroad", 5);
List<String> whiteRavens = bookTitlesByCount.get(1);
// print them
}
private static void addBookTitle(String title, int count) {
List<String> bookTitles = bookTitlesByCount.get(count);
if (bookTitles == null) {
bookTitles = new ArrayList<>();
bookTitlesByCount.put(count, bookTitles);
}
bookTitles.add(title);
}
Filter all entries of the map, create a list from the map's keys, these are your book titles. Pass that list to printWhiteRavens which then prints the titles:
public static void main(String[] args) {
Map<String,Integer> map = createBooksCounter();
List<String> list = map.entrySet().stream().
filter(e->e.getValue()==1).
map(e->e.getKey()).
collect(Collectors.toList());
printWhiteRavens(list);
}
public static void printWhiteRavens(List<String> whiteRavens) {
whiteRavens.stream().forEach(System.out::println);
}
Also you have to edit your createBooksCounter method to return the map using
return createBooksCounter;
Related
I know this question will be pretty amateur but, I having trouble understanding why my hashmap will not store or retrieve values when I use the same object instance as a key. My code is as follows
public class Candidate {
private String id;
private String name;
public Candidate (String id, String name){
this.id = id;
this.name = name;
}
public static void main(String args[]){
Candidate cad = new Candidate("101","hari");
HashMap<Candidate,String> mp = new HashMap<Candidate,String>();
mp.put(cad, "sachin");
mp.put(cad, "shewag");
for(Candidate cand : mp.keySet()){
System.out.println(mp.get(cand).toString());
}
}
I am overriding hashcode and equals as follows.
#Override
public boolean equals(Object obj){
Candidate cad =(Candidate)obj;
if(!(obj instanceof Candidate)){
return false;
}
if(cad.id.equals(this.id) && cad.name.equals(this.name)){
return true;
}
return false;
}
#Override
public int hashCode(){
return Objects.hash(id, name);
}
When I try to get the size of the hashmap, it returns as only one. meaning the first insertion into the hashmap was overridden by the second one.
Is it because I am using the same instance of Candidate to insert two values? Is it possible to force hashmap to insert both key,value pairs?
The whole idea behind a Map is that 1) keys are unique -- it holds only one key/value pair for a particular key, and 2) its look up is relatively "cheap".
You've only got one object within your HashMap. Understand that when you add another key, value pair to the map, if the key is the same as a previous item in the map, the previous item is replaced by the new one. If you want to add two or more items, then use different keys, or create a Map that holds a List<...> of objects as its value. e.g.,
HashMap<Candidate, List<String>>
In this situation, you would first check to see if the Map holds a Candidate item, and if so, add a new String to its list. If not, then add the new Candidate with a new ArrayList<String> value. Usually I use a method for just this purpose, something like:
public static void put(Candidate cand, String text) {
if (newMap.containsKey(cand)) {
newMap.get(cand).add(text);
} else {
List<String> list = new ArrayList<>();
list.add(text);
newMap.put(cand, list);
}
}
And yes, as d.j.brown states in comment, fix your equals method to avoid a class cast exception.
Something like so:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public class MyCandidateTest {
private static Map<Candidate, List<String>> newMap = new HashMap<>();
public static void main(String args[]) {
Candidate cad = new Candidate("101", "hari");
put(cad, "Foo");
put(cad, "Bar");
for (Candidate cand : newMap.keySet()) {
System.out.println(newMap.get(cand).toString());
}
}
public static void put(Candidate cand, String text) {
if (newMap.containsKey(cand)) {
newMap.get(cand).add(text);
} else {
List<String> list = new ArrayList<>();
list.add(text);
newMap.put(cand, list);
}
}
}
public class Candidate {
private String id;
private String name;
public Candidate(String id, String name) {
this.id = id;
this.name = name;
}
#Override
public boolean equals(Object obj) {
// Candidate cad =(Candidate)obj; // !! no
if (!(obj instanceof Candidate)) {
return false;
}
Candidate cad = (Candidate) obj; // !! yes
if (cad.id.equals(this.id) && cad.name.equals(this.name)) {
return true;
}
return false;
}
#Override
public int hashCode() {
return Objects.hash(id, name);
}
}
There is a simpler way to do what you want with java-8 btw, simplified example:
HashMap<String, List<String>> mp = new HashMap<>();
List<String> list = Arrays.asList("aa", "aa", "bb", "bb");
for (String s : list) {
mp.computeIfAbsent(s, k -> new ArrayList<>()).add("c");
}
System.out.println(mp); // {bb=[c, c], aa=[c, c]}
Either use
Map<Candidate, List<String>> or
A good 3rd party alternative such as Google's Multimap: https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html
So I have this data
Key Value
--- -----
fruit apple
fruit banana
fruit grapes
cars honda
cars lexus
cars bmw
schools harvard
schools yale
...
And I would like to construct the data to a Map<String, Collections<String>> and make a method that adds all the data. So far, I have a constructor that instantiates my Map variable
public Map<String, Collection<String>> keys;
public newMap() {
keys = new HashMap<>();
}
public void addkeys(String K, String V) {
Collection<String> names = new HashSet<String>();
if (keys.containsKey(K) && !keys.values().contains(V)) {
names.add(V);
courses.put(K, names);
} else if (!keys.containsKey(K) && !keys.values().contains(V)) {
names.add(V);
courses.put(student, classes);
}
}
So when I run my test
newMap.addkeys("fruit", "apple");
newMap.addkeys("fruit, "banana");
newMap.addkeys("fruit", "grapes");
newMap.addkeys("cars, "honda");
newMap.addkeys("cars", "lexus");
newMap.addkeys("cars, "bmw");
newMap.addkeys("schools", "harvard");
newMap.addkeys("schools, "yale");
it should return
fruit = [apple, banana, grapes]
cars = [honda, lexus, bmw]
schools = [hardvard, yale]
but instead I get
fruit = [grapes]
cars = [bmw]
schools = [yale]
it seems like it's only adding the last instance because whenever I call Collection<String> names = new HashSet<String>(), i'm re-instantiating names but when I this instantiation to the begininng of the class, It just adds on everything. So it returns fruit = [apple, banana, grapes, honda, lexus, bmw, hardvard, yale].
Pre-Java 8:
Use the fact that Map#get returns null when there is not a mapping in order to determine if you need to put a new collection in the map, then add the value to the collection.
public void addkeys(String K, String V) {
Collection<String> values = keys.get(K);
if (values == null) {
values = new HashSet<>();
keys.put(K, values);
}
values.add(V);
}
Java 8+:
Use Map#computeIfAbsent to add a new collection to the map if there is not a mapping, and then add the value to the returned collection.
public void addkeys(String K, String V) {
keys.computeIfAbsent(K, k -> new HashSet<>()).add(V);
}
You can rewrite your function like this:
public void addkeys(String K, String V) {
if (keys.containsKey(K)) {
keys.get(K).add(V); // add to existing hashset
} else {
Collection<String> names = new HashSet<String>();
names.add(V);
keys.put(K, names); // add new hashset for key
}
}
Try this
public Map<String, Set<String>> myMap;
public newMap() {
myMap = new HashMap<>();
}
public void putInMap(String key, String val) {
if (myMap.containsKey(key)) {
myMap.get(K).add(V);
} else {
Set<String> values = new HashSet<String>();
values.add(val);
myMap.put(key, values);
}
}
I am struggling to find a way to dispatch this to functions in java8
Person p = registry.getPerson();
if (field == Field.LASTNAME) {
p.setLastName(str);
}
if (field == Field.FIRSTNAME) {
p.setFirstName(str);
}
if (field == Field.MIDDLENAME) {
p.setMiddleName(str);
}
My idea is to use some kind of function dispatch table to replace the if statements in the case of more cases:
Map<Integer, Function> map = new HashMap<Integer, Function>
static {
map.put(1, new Function<String, String>() {
#Override
public Object apply(String str) {
person.setLastName(str);
return str;
}
}
}
But the code cannot compile, because i need to pass the person object some place. Anyone knows a pattern for this?
Assuming Field is an enum, you can add BiConsumer<Person,String> as an enum field:
class Person {
static enum Field {
FIRSTNAME(Person::setFirstName),
MIDDLENAME(Person::setMiddleName),
LASTNAME(Person::setLastName)
;
private BiConsumer<Person, String> setter;
private Field(BiConsumer<Person, String> setter) {
this.setter = setter;
}
}
public void set(Field field, String str) {
field.setter.accept(this, str);
}
......
}
Instead of storing Function<String,String>, you can store BiFunction<Person,String,String> and pass the Person instance in as a parameter.
Map<Integer, BiFunction<Person,String,String>> map =
new HashMap<Integer, BiFunction<Person,String,String>>();
static {
map.put(1, (person, str)->person.setLastName(str));
}
In the interest of simplicity, you could also just store a List of the functions, if you're just going to index them by an integer, it's faster for random access and makes for less complicated generic code:
List<BiFunction<Person,String,String>> list = new ArrayList<BiFunction<Person,String,String>>();
static {
list.add((person, str)->person.setLastName(str));
}
I wanted to create a table/list in Java, and I wonder what is the best way to handle it.
The table should have a structure like this:
Term propertyList entitiesList
a1 p1=1, p2=2, p3=2 T1,T2
a2 p5=0, p4=5 ,p3=3 T2,T1
a3 p1=1 ,p4=3, p3=9 T3,T1,T2
...
a10
I have a list with exactly 10 terms, and for every term there is a list of properties (deep with key and value), and the properties can be either in one or more entities.
I need some help on how to create it, e.g. should I use list, map, collection etc.
How can I add hardcoded values to them as literals in the code, and what is the best way to read data from it, taking into account performance, given that later I will need to use this for every entity and find the related properties that participate in every term.
first off Create Term class.
So you have list of Terms: List<Term>
Term class
public class Term {
private String mName = "";
private Map<String, Integer> mPropertyMap = new LinkedHashMap<String, Integer>();
private List<String> mEntitiesList = new ArrayList<String>();
public Term(String name) {
mName = name;
}
public void generate(Map<String, Integer> propertyMap, List<String> entitiesList) {
mPropertyMap = propertyMap;
mEntitiesList = entitiesList;
}
public Map<String, Integer> getPropertyMap() {
return mPropertyMap;
}
public void setPropertyMap(Map<String, Integer> propertyMap) {
this.mPropertyMap = propertyMap;
}
public List<String> getEntitiesList() {
return mEntitiesList;
}
public void setEntitiesList(List<String> entitiesList) {
this.mEntitiesList = entitiesList;
}
public String getName() {
return mName;
}
public void setmName(String name) {
this.mName = name;
}
}
Main Class
public class MyClass {
private List<Term> mTermList = null;
private void init() {
mTermList = new ArrayList<Term>();
}
private void addSomeTerm() {
Map<String, Integer> propertyMap = new LinkedHashMap<String, Integer>();
propertyMap.put("p1", 1);
propertyMap.put("p2", 2);
propertyMap.put("p3", 3);
List<String> entitiesList = new ArrayList<String>();
entitiesList.add("T1");
entitiesList.add("T2");
Term term = new Term("a1");
term.generate(propertyMap, entitiesList);
mTermList.add(term);
}
private String printTerms() {
StringBuilder buff = new StringBuilder();
for(Term currTerm : mTermList){
buff.append(currTerm.getName()).append(" ");
Map<String, Integer> propertyMap = currTerm.getPropertyMap();
Set<String> sets = propertyMap.keySet();
Iterator<String> itr = sets.iterator();
String key = null;
Integer value = null;
while(itr.hasNext()){
key = itr.next();
value = propertyMap.get(key);
buff.append(key + "=" + value).append(",");
}
buff.setLength(buff.length()-1); // remove last ','
buff.append(" ");
List<String> entitiesList = currTerm.getEntitiesList();
for(String str : entitiesList){
buff.append(str).append(",");
}
buff.setLength(buff.length()-1); // remove last ','
}
return buff.toString();
}
/**
* #param args
*/
public static void main(String[] args) {
MyClass m = new MyClass();
m.init();
m.addSomeTerm();
System.out.println(m.printTerms());
}
}
Output:
a1 p1=1,p2=2,p3=3 T1,T2
It looks like you could have the following structure:
class Term {
String id;
Map<String, String> properties;
List<Entity> entities; // (or Set<Entity> if no duplicates are allowed)
}
But it's not very clear what you mean by "deep" and by "the properties can be either in one or more entities".
I need a mapping from a list of keys to a value. I know I could write my own code like this:
Map<Person, Map<Daytime, Map<Food, Integer>>> eaten = ...;
Now I want to have some get and put methods like these:
Integer numberOfEggsIAteInTheMorning = eaten.get(me, morning, scrambledEggs);
eaten.put(me, evening, scrambledEggs, 1);
Do you know of an existing class that has this kind of API? I'm too lazy of writing it myself. ;)
If you look for a more generic approach, and you might have more than 2 or 3 'chain steps', I would suggest in applying some different structural approach, rather than sticking to using only basic collection classes. I have feeling that Composite Pattern could be the right choice if it's correctly applied.
EDIT: due to example requested
The full example would be somewhat time consuming, so let me just explain my idea with dirty Java/pseudocode mix (I'm not even sure if I've missed something!!!). Let's consider we have class BaseMap:
abstract class BaseMap {
public abstract Object getValue(Object.. keys);
public abstract void putValue(Object value, Object.. keys);
}
Then we could have ObjectMap that would be the 'leaf' of our composite structure:
class ObjectsMap extends BaseMap {
private Map<Object, Object> map = new [...]
public Object getValue(Object.. keys) {
// assert that keys.length == 1
return map.get(keys[0]);
}
public void putValue(Object value, Object.. keys) {
// assert that keys.length = 1
map.put(keys[0], value);
}
}
And the actual composite would be as such:
class CompositeMap extends BaseMap {
private Map<Object, BaseMap> compositeMaps = new [...]
public Object getValue(Object.. keys) {
// assert that keys.length > 1
return compositeMap.get(keys[0]).getValue(/* System.arrayCopy => subset of elements {keys_1, .. ,keys_max} */);
}
public void putValue(Object value, Object.. keys) {
// assert keys.length > 1
BaseMap newMap = null;
if (keys.length = 2) -> newMap = new ObjectsMap()
else newMap = new CompositeMap();
newMap.putValue(value, /*subset of keys {keys_1, .. , keys_max}*/);
}
}
You can use org.apache.commons.collections.keyvalue.MultiKey for that: Map<Multikey, Object>
It would be hard to implement a general chained map.
How would the declaration of the class look like? (You can't have a variable number of type parameters.
class ChainedMap<K1..., V>
Another option would be to have a ChainedMapUtil class that performs put / get recursively.
Here is an example of a recursive get. (Quite ugly solution though I must say.)
import java.util.*;
public class Test {
public static Object chainedGet(Map<?, ?> map, Object... keys) {
Object k = keys[0];
if (!map.containsKey(k)) return null;
if (keys.length == 1) return map.get(k);
Object[] tailKeys = Arrays.copyOfRange(keys, 1, keys.length);
return chainedGet((Map<?,?>) map.get(k), tailKeys);
}
public static void main(String[] arg) {
Map<String, String> m1 = new HashMap<String, String>();
m1.put("ipsum", "dolor");
Map<Integer, Map<String, String>> m2 =
new HashMap<Integer, Map<String, String>>();
m2.put(17, m1);
Map<String, Map<Integer, Map<String, String>>> chained =
new HashMap<String, Map<Integer, Map<String, String>>>();
chained.put("lorem", m2);
System.out.println(chainedGet(chained, "lorem", 17, "ipsum")); // dolor
System.out.println(chainedGet(chained, "lorem", 19, "ipsum")); // null
}
}
If you are going to write your own, I would suggest
eaten.increment(me, evening, scrambledEggs);
You could use a composite key
eaten.increment(Key.of(me, evening, scrambledEggs));
(TObjectIntHashMap supports increment and adjust)
You may not even need a custom key.
eaten.increment(me + "," + evening + "," + scrambledEggs);
It is fairly easy to decompose the key with split()
I once made a map using 3 keys just for fun.May be you can use it instead of using chained maps:
public class ThreeKeyMap<K1,K2,K3,V>{
class wrap{
K1 k1;
K2 k2;
K3 k3;
public wrap(K1 k1,K2 k2,K3 k3) {
this.k1=k1;this.k2=k2;this.k3=k3;
}
#Override
public boolean equals(Object arg0) {
// TODO Auto-generated method stub
wrap o=(wrap)arg0;
if(!this.k1.equals(o.k1))
return false;
if(!this.k2.equals(o.k2))
return false;
if(!this.k2.equals(o.k2))
return false;
return true;
}
#Override
public int hashCode() {
int result=17;
result=37*result+k1.hashCode();
result=37*result+k2.hashCode();
result=37*result+k3.hashCode();
return result;
}
}
HashMap<wrap,V> map=new HashMap<wrap, V>();
public V put(K1 k1,K2 k2,K3 k3,V arg1) {
return map.put(new wrap(k1,k2,k3), arg1);
}
public V get(Object k1,Object k2,Object k3) {
return map.get(new wrap((K1)k1,(K2)k2,(K3)k3));
}
public static void main(String[] args) {
ThreeKeyMap<Integer,Integer,Integer,String> birthDay=new ThreeKeyMap<Integer, Integer, Integer, String>();
birthDay.put(1, 1,1986,"Emil");
birthDay.put(2,4,2009, "Ansih");
birthDay.put(1, 1,1986,"Praveen");
System.out.println(birthDay.get(1,1,1986));
}
}
UPDATE:
As #Arturs Licis suggested.I looked up in net for composite pattern and I wrote a sample using it.I guess this is composite..Please comment if it is not so.
Person class:
public class Person {
private final String name;
private Map<Time, Food> map = new HashMap<Time, Food>();
public Person(String name) {
this.name = name;
}
void addTimeFood(Time time, Food food) {
map.put(time, food);
}
public String getName() {
return name;
}
Food getFood(Time time) {
Food tmp = null;
return (tmp = map.get(time)) == null ? Food.NoFood : tmp;
}
// main to test the person class
public static void main(String[] args) {
Person p1 = new Person("Jack");
p1.addTimeFood(Time.morning, Food.Bread);
p1.addTimeFood(Time.evening, Food.Chicken);
Person p2 = new Person("Jill");
p2.addTimeFood(Time.morning, Food.Egg);
p2.addTimeFood(Time.evening, Food.Rice);
Map<String, Person> map = new HashMap<String, Person>();
map.put(p1.getName(), p1);
map.put(p2.getName(), p2);
System.out.println(map.get("Jack").getFood(Time.evening));
}
#Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append(name).append("\n");
b.append(map);
return b.toString();
}
}
Food class:
public enum Food {
Rice,
Egg,
Chicken,
Bread,
NoFood;
}
Time class:
public enum Time {
morning,
evening,
night
}