Adding items to empty List at specific locations in java - java

Is there any way I can make the below code work without commenting the 3rd line.
List<Integer> list = new ArrayList<Integer>();
list.add(0,0);
//list.add(1,null);
list.add(2,2);
I want to add items to list at specific locations. But if I don't change the index to Nth position I am not being able to add at Nth as told in this answer.
I can't use a map because I don't want to miss a value when the keys are same. Also adding null values to a list for large lists will be an overhead. When there is a collision I want the item to take the next position(nearest to where it should have been).
Is there any List implementation that shifts index before it tries to add the item?

Use something like a MultiMap if your only concern is not "missing a value" if the keys are the same.
I'm not sure how doing a shift/insert helps if I understand your problem statement--if the "key" is the index, inserting will lose the same information.

You can use Vector and call setSize to prepopulate with null elements.
However, your comment about the overhead of the nulls speaks to an associative container as the right solution.

This still smells like you should be using a Map. Why not use a Map<Integer, List<Integer>>?
something like,
private Map<Integer, List<Integer>> myMap = new HashMap<Integer, List<Integer>>();
public void addItem(int key, int value) {
List<Integer> list = myMap.get(key);
if (list == null) {
list = new ArrayList<Integer>();
myMap.put(key, list);
}
list.add(value);
}
public List<Integer> getItems(int key) {
return myMap.get(key);
}

Well, There are a couple of ways I would think to do this, if you are not adding items too frequently, then it might be a good idea to simply do a check to see if there is an item at that location before adding it.
if(list.get(X) == null)
{
list.add(X,Y);
}
Otherwise if you are going to be doing this too often...then I would recommend creating your own custom List class, and extending ArrayList or whatever you are using, and simply override the add method, to deal with collisions.

Related

Adding a String to a StringList while it is part of a hashmap value

Say I have Map<List<String>, List<String>> whatComesNext,
And while in a for loop, for every iteration I want to add the nth element of List<String> text to the value of whatComesNext. Why can I not perform whatComesNext.put(key, whatComesNext.get(key).add(text.get(n)))? The idea would be to retrieve the value from its respective key in the hashmap and add my desired String to it. This is assuming that every key in the hashmap has a value.
Below is my full code:
static void learnFromText(Map<List<String>, List<String>> whatComesNext, List<String> text) {
for (int i=0; i<=text.size()-3; i++) {
if (whatComesNext.containsKey(Arrays.asList(text.get(i),text.get(i+1)))==false) {
whatComesNext.put(Arrays.asList(text.get(i),text.get(i+1)), Arrays.asList(""));
}
whatComesNext.put(Arrays.asList(text.get(i),text.get(i+1)), whatComesNext.get(Arrays.asList(text.get(i),text.get(i+1))).add(text.get(i+2)));
}
}
The Arrays.asList() looks complicated, but it's because I was getting null maps when trying to intialize my own String Lists to try and hold my keys and values, which someone told me was because I was repeatedly clearing the lists that the keys & values were assigned to, leaving them null. I thought I'd solve that problem by referring directly to the original List<String> text, because that remains unchanged. The idea is to first check if a key is not present in the map, and if so assign it an empty List as a value, and then add a String from text to the value of the map.
The error I get when running the code is Error: incompatible types: boolean cannot be converted to java.util.List<java.lang.String> in the line whatComesNext.get(Arrays.asList(text.get(i),text.get(i+1))).add(text.get(i+2)));. I don't understand where this could go wrong, because I don't see which method is returning a boolean.
The error comes from the fact that List.add(Object o) returns a boolean and not the List itself. The Map is declared to contain instances of List<String> as value. If you simply want to add a value to a list, just retrieve it from the map and call add on it. Check the result of the get-process for null and create a new list and put it into the Map if that's the case
I can see a couple of other problems as well:
You call Arrays.asList(...) multiple times creating multiple lists with the same elements. This is a major performance issue and you're just lucky, that the returned list is actually implementing equals, so that your logic is actually working (I expected that to be the problem of your "doesn't work"-description before you updated it.
If the key doesn't exist, you're creating a List containing an empty text. If that should be an empty list, that's not what you're doing and you might run into problems later on, when you work with text-values (that is the empty text as first element) that weren't part of the original input values.
Without changing the type of the key of the Map a - in my eyes - better implementation would look like this:
static void learnFromText(Map<List<String> whatComesNext, List<String>, List<String> text) {
for (int i=0; i<= text.size() - 3; i++) {
List<String> listKey = text.subList(i, i+2);
List<String> value = whatComesNext.get(listKey);
if (value == null) {
value = new ArrayList<>();
whatComesNext.put(listKey, value);
}
value.add(text.get(i+2)));
}
}
The calculation of the list for the keys happens only once, increasing performance and reducing the need of resources. And I think it's more readable that way as well.
The .add() method returns a boolean, your parenthesis are misplaced, replace your last line with this one:
whatComesNext.put(Arrays.asList(text.get(i),text.get(i+1)), whatComesNext.get(Arrays.asList(text.get(i),text.get(i+1)))).add(text.get(i+2));

How To Access hash maps key when the key is an object

I cannot seem to figure out how to access the values of my hashmap
What I am basically trying to do is create a hashmap with an array as one of the values like json style.. If that makes sense?
So I want something like hash{key: value1, value2, value3, [number1,number2]}
and be able to access it like (pseudocode:) hash.get(3).get(1)
public class WebSearch {
readFile.ReadFile xfile = new readFile.ReadFile("inputgraph.txt");
HashMap webSearchHash = new HashMap();
ArrayList belongsTo = new ArrayList();
ArrayList keyphrase = new ArrayList();
public WebSearch() {
}
public void createGraph()
{
HashMap <Object, ArrayList<Integer> > outlinks = new HashMap <Object, ArrayList<Integer>>();
for (int i = 0; i < xfile.getNumberOfWebpages(); i++ )
{
keyphrase.add(i,xfile.getKeyPhrases(i));
outlinks.put(keyphrase.get(i), xfile.getOutLinks(i));
}
}
keyphrases is an ArrayList
this is my output of System.out.print(outlinks);
{[education, news, internet]=[0, 3], [power, news]=[1, 4], [computer, internet, device, ipod]=[2], [university, education]=[5]}
How would I go about getting say just this: [education, news, internet]=[0, 3]
I have tried:
outlinks.get(xfile.getKeyPhrases(i))
xfile.getKeyPhrases(0) would for example return [education, news, internet]
You can get the key set (Map.keySet()) of the map first; outlinks.keySet()
Then you can use these keys on your map to get your entries (values of the keys)
You haven't posted enough of the surrounding code for your question to be entirely clear, but look at the Javadocs for Map. You will probably get what you want by iterating over outlinks.values().
I recommend to use a customized object and use it inside your collections.
You may create a POJO/Bean class and overwrite the toString method with the details that you want, for instance the a iterate over items inside a array.
When you use it to print or display the toString method will be call.
The following link show you some ideas:
http://www.javapractices.com/topic/TopicAction.do?Id=55
http://en.wikipedia.org/wiki/Plain_Old_Java_Object
You can access the keys of any HashMap using Map.keySet() method.
Also note that java.util.HashMap is unordered. HashMap makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
You would like to relook at the structure of your HashMap, you are having ArrayList as your key.

how to calculate efficent how many elements in a list are the same?

I need to do the following task:
I have a list with items.
Each of the items also have a List with strings like "gkejgueieriug"
Now I need to run throw the list and check how many of the items in the list of each item are also in the current element
here is a small pseudeo code:
OneItem;
List AllItems;
for Item in AllItems:
int count = number strings in Item.Values which are also in OneItem.Values
because the data is very big, I need some help to make a efficent implementation.
How to do this? Should I use a hashmap? how to count the overlap?
Your question doesn't provide detailed information about the involved types which you want to compare. So I assume you have a List<Item>. Each item has a String and an own List<Item>
So first I would create a HashSet of the Strings of the Items in your AllItems-List. Iterate the AllList and add the String of each Item to the HashSet.
Then in the second step iterate the AllList again and iterate the List in the Items and check each String here if it is in the HashSet which was created before.
If you have to check this several times you can keep the HashSet as a cache which you refresh when the AllList gets changed.
// Step 1: Create Set of Strings
Set<String> allStrings = new HashSet<String>();
for (Item item : allList) {
allStrings.add(item.getString());
}
// Step 2: Calculate occurrences
for (Item item : allList) {
for (Item internalItem : item.getItems()) {
if (allStrings.contains(internalItem.getString()) {
// Count one up for this String
// This might be done by replacing the HashSet by a HashMap and use its values for counting
}
}
}
Make Item.Values a Set rather than a List. A decent Set implementation - like a HashSet - will run the contains() operation in constant time. Then iterate over one set and increment a count each time the other set contains the element.
An optimization is to always iterate over the smaller set. That way the counting operation is O(n) where n is the size of the smaller set.
If the comparison is only one way (i.e. only counting strings in one list that are also in another but NOT the other way around) then the best way of doing it would probably be to put both lists in a Set instead:
HashSet firstSet = ...
HashSet secondSet = ...
for(each value in firstSet)
{
if(secondSet.contains(value)
{
// Do what you want with the value.
// Sugestion: Add value to a separate set
// so you can track duplicates etc
}
}
With this code you create an ArrayList of Map with the string values and the number of matches in your OneItem.Values...
ArrayList<Map<String,Integer>> matches=new ArrayList<>();
for (Item i : AllItems) {
Map<String,Integer> map=new HashMap<>();
for(String s:values){
map.put(s,Collections.frequency(OneItem.Values, s));
}
matches.add(map);
}

How better to test and create a new list, if it does not exist in List<List<T>> structure?

How better to test and create a new list, if it does not exist?
I only found this solution:
private List<List<Action>> actionList = Lists.newArrayList();
...
f(int index){
Optional<List<Action>> optionalActionList = Optional.fromNullable(actionList.get(index));
if (!optionalActionList.isPresent()) {
actionList.add(new ArrayList<Action>());
}
actionList.get(index).add(index, new Action());
}
Maybe I should change the structure of storage? (The order of addition. Ability to add both the beginning and the end of the list. Are important in both lists).
Can use Google Guava if need.
It's not clear what the Optional is really buying you here. I see the value of Optional as communicating between methods, or in storage.
Additionally, I don't think the code is doing what you think it is - you're asking for the list at a particular index, but then if the value at that index is null, you're adding a new list at the end. It sounds like you want:
f(int index){
List<Action> list = actionList.get(index);
if (list == null) {
list = new ArrayList<Action>();
actionList.set(index, list);
}
// Note: no need to use index at all here... potentially
list.add(new Action());
}
Note that I'm not using add(index, new Action()) at the end - at it was very unclear why you'd want to do so. You've already got to the right list using index - why would you use index again?
The above assumes that the "outer" list is already the right size, but filled with nulls for "absent" lists. If that's not the case, you'll need to detect when index is beyond the bounds of the current outer list.
Basically, if the above doesn't help, please give more information as your question is currently unclear.
If you're not on Java 7 instead of
list = new ArrayList<Action>();
you can use
list = Lists.newArrayList();
if you add a static import to this you can shorten it to
list = newArrayList();

removing duplicates from an arraylist

I am trying to remove duplicate objects from an arraylist
see code below:
ArrayList<Customer> customers=new ArrayList<Customer>();
for(int i=0;i<accounts.size();i++){
customers.add(accounts.get(i).getCustomer());
}
for(int i=0;i<customers.size();i++){
for(int j=i+1;j<customers.size();j++){
if(customers.get(i).getSocialSecurityNo().compareTo(customers.get(j).getSocialSecurityNo())==0){
if(customers.get(i).getLastName().compareToIgnoreCase(customers.get(j).getLastName())==0){
if(customers.get(i).getFirstName().compareToIgnoreCase(customers.get(j).getFirstName())==0){
customers.remove(j);
}
}
}
}
}
However, it seems that the last object in the list is not being processed. Perhaps someone can pinpoint the error
Try adding j--; after removing an item. That will reindex for you and solve your issue.
The basic flaw is that since the ListArray is mutable, once you remove one element your indexes have to be readjusted.
if(customers.get(i).getFirstName().compareToIgnoreCase(customers.get(j).getFirstName())==0){
customers.remove(j--);
}
also try subtracting one from your i loop:
for(int i=0;i<customers.size()-1;i++){
for(int j=i+1;j<customers.size();j++){
public static void removeDuplicates(ArrayList list) {
HashSet set = new HashSet(list);
list.clear();
list.addAll(set);
}
override equals and hashcode appropriatley
custormers = new ArrayList(new HashSet(customers))
ensure the equals and hashmethod are correctly implemented
The code below worked for me. Give it a try. You can manipulate the compare method to suit your taste
ArrayList customers = .....;
Set customerlist = new TreeSet(new Comparator(){
#Override
public int compare(Customer c1, Customer c2) {
return c1.getSocialSecurityNo().compareTo(c2.getSocialSecurityNo());
}
});
customerlist.addAll(customers);
customers.clear();
customers.addAll(customerlist);
It's your int j=i+1 that causes trouble. You need to test with the last value of the customers list for each iteration.
Before you add them to the list in the above loop, why don't you check
if(!cutomers.contains(accounts.get(i).getCustomer())
{
//add them if it doesn't contain
}
It should save you from doing the second loop
Edit: Need to override the equals method.
So, about doing this right:
Your Customer objects should have an equals() and hashCode() method, which do the comparison. (Or you simply would have only one Customer object for each customer, which would mean your data model would have to be adjusted. Then the default hashCode/equals would do.)
If you have this, you can replace your three nested ifs with one:
if(customers.get(i).equals(customers.get(j)) {
customers.remove(j);
}
This would not yet solve your problem, but make it easier to have a clearer look on it. If
you look at which objects are compared to which others, you will see that after each removal
of an object from the list, the next one has the same index as the one which you just removed,
and you will not compare the current object to it. As said, j-- after the removal will solve this.
A more performant solution would be using a Set (which is guaranteed not to contain duplicates).
In your case, a HashSet<Customer> or LinkedHashSet<Customer> (if you care about the order)
will do fine.
Then your whole code comes down to this:
Set<Customer> customerSet = new HashSet<Customer>();
for(Account acc : accounts){
customerSet.add(acc.getCustomer());
}
List<Customer> customers = new ArrayList<Customer>(customerSet);
If you don't really need a list (i.e. indexed access), ommit the last line and simply
use the set instead.
My first thought was to use Sets, as others have mentioned. Another approach would be to use Java's version of the foreach, instead of using indexes. A general approach:
public static ArrayList removeDuplicates(ArrayList origList) {
ArrayList newList = new ArrayList();
for (Object m : origList) {
if (!newList.contains(m)) {
newList.add(m);
}
}
return newList;
}
In testing, I just used Strings; I'd recommend inserting Customer into the code where appropriate for type safety.

Categories

Resources