Reputation: 29463
I have a HashMap that maps companies to an ArrayList of the products they sell, like so:
thiscompany --> [productA, productB...]
thatcompany --> [productC, productA...]
Hence it is very easy to generate a list of products given a specific company. Note that multiple companies may sell the same product. The issue is that I also need to, given a specific product, find all the companies that sell it. And quickly. This sort of lookup may happen once, or multiple times. I am wondering the most efficient way to provide this functionality.
Currently I am generating a new data structure by iterating through each ArrayList and mapping each product to its vendor. This is expensive though, because I have to check to see whether the HashMap I am creating contains that product as a key each time before adding, plus it requires me to get the each ArrayList, add the new vendor, delete the old ArrayList then map the new one for each entry. I simply cannot see a faster way of doing this though, perhaps someone can provide me with some insight?
Upvotes: 3
Views: 657
Reputation: 13789
Try MultiMaps.invertFrom,
Example:
Multimap<String, Integer> map = HashMultimap.create();
map.put("a", 3);
map.put("a", 4);
map.put("a", 5);
map.put("b", 5);
map.put("b", 3);
map.put("b", 6);
Multimap<Integer,String> mapInverse=HashMultimap.create();
Multimaps.invertFrom(map, mapInverse);
System.out.println(mapInverse);
Output:
{3=[b, a], 4=[a], 5=[b, a], 6=[b]}
Alternative Solution:
Here I'm creating a 2D boolean array representing the company and their products for easy lookup.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.collect.TreeMultimap;
public class ProductCompanyMap {
private Multimap<String, String> companyProducts = TreeMultimap.create();
private boolean[][] ProductCompanyTable;
private Map<String,Integer> productIndexMap = new HashMap<String, Integer>();;
private Map<String,Integer> companyIndexMap = new HashMap<String, Integer>();
private String[] productArray;
private String[] companyArray;;
{
companyProducts.put("Britania","Biscuts");
companyProducts.put("Britania","Soap");
companyProducts.put("Britania","Cloths");
companyProducts.put("MicroSoft","Software");
companyProducts.put("Birla","Cloths");
companyProducts.put("Birla","Software");
}
public ProductCompanyMap(){
Set<String> companyNames=companyProducts.keySet();
Set<String> productNames= Sets.newTreeSet(companyProducts.values());
companyArray = companyNames.toArray(new String[companyNames.size()]);
createIndexMap(companyIndexMap, companyArray);
productArray = productNames.toArray(new String[productNames.size()]);
createIndexMap(productIndexMap,productArray);
ProductCompanyTable = new boolean[companyArray.length][productArray.length];
for(int i=0;i<companyArray.length;i++)
for(int j=0;j<productArray.length;j++){
if(companyProducts.containsEntry(companyArray[i],productArray[j]))
ProductCompanyTable[i][j] = true;
}
}
private void createIndexMap(Map<String,Integer> map,String[] arr){
for(int i=0;i<arr.length;i++)
map.put(arr[i], i);
}
public List<String> getProductsOfCompany(String companyName){
List<String> productsOfCompany = new ArrayList<String>();
Integer companyIndex = null;
if((companyIndex=companyIndexMap.get(companyName))!=null)
{
for(int i=0;i<ProductCompanyTable[companyIndex].length;i++)
if(ProductCompanyTable[companyIndex][i])
productsOfCompany.add(productArray[i]);
}
return productsOfCompany;
}
public List<String> getCompanysWithProduct(String productName){
List<String> companysWithProduct = new ArrayList<String>();
Integer productIndex = null;
if((productIndex=productIndexMap.get(productName))!=null)
{
for(int i=0;i<ProductCompanyTable.length;i++)
if(ProductCompanyTable[i][productIndex])
companysWithProduct.add(companyArray[i]);
}
return companysWithProduct;
}
public static void main(String[] args) {
ProductCompanyMap mm=new ProductCompanyMap();
System.out.println("Products of Birla : " +mm.getProductsOfCompany("Birla"));
System.out.println("Company's producing cloths : "+mm.getCompanysWithProduct("Cloths"));
}
}
Upvotes: 1
Reputation: 8677
Why don't you create the map of companies that sell a products at the same time you build the map of products for company
Map<Product, Set<Company>> companiesByProduct
Map<Company, Set<Product>> productsByCompany
public void add(Company company, Product product) {
Set<Company> companies = companiesByProduct.get(product);
if (companies==null) {
companies = new HashSet<Company>();
companiesByProduct.put(product, companies);
}
companies.add(company);
// do the same for
Set<Product> products = productsByCompany.get(product);
....
Or to create new companiesByProduct Map from the map received by the server you could use (you may need to adjust depending on the exactly type of your original map):
for (Company company : originalMap.keySet()) {
for (Product product : originalMap.get(company)) {
Set<Company> companies = companiesByProduct.get(product);
if (companies==null) {
companies = new HashSet<Company>();
companiesByProduct.put(product, companies);
}
companies.add(company);
}
}
Upvotes: 1
Reputation: 234795
Maintain two data structures—the HashMap you maintain now and another one that maps products to companies:
productA --> [thiscompany, thatcompany...]
productC --> [thatcompany, othercompany...]
Upvotes: 0
Reputation: 44821
How about changing the ArrayList to a HashSet.
List<String> findCompanies(Map<String,Set<String>> companyToProducts, String product) {
List<String> companies = new ArrayList<String>();
for (Map.Entry<String,Set<String>> entry : companyToProducts) {
Set<String> products = entry.getValue();
if (products.contains(product)) {
companies.add(entry.getKey());
}
}
return companies;
}
Another common approach would be to use a table in a database with a column for product and a column for company and then do a:
select distinct company from companyToProduct where product = 'cheese';
Upvotes: 2