I have a two ArrayList given below as sample.
AccVO:
compareKey,Amount fields
List 1:
AccVO[001,500]
AccVO[002,600]
AccVO[003,800]
List2:
AccVO[001,100]
AccVO[001,100]
AccVO[001,300]
AccVO[003,300]
AccVO[003,300]
AccVO[003,200]
AccVO[005,300]
AccVO[005,300]
I have sorted the two lists.
I have to compare the two lists with the compare key and fetch the records of List2 to insert into database.
Sample Code:
for(AccVO accvo1 : List1){
for(AccVO accvo2 : List2){
if(accvo1.getCmpkey().equals(accvo2.getCmpkey())){
//insert the recs into the table
}
}
}
Since my list size will be larger i.e handling millions of records, i need some optimistic logic in looping the records.
Thanking you in advance
Prasanna
Because your lists are sorted, you can use an index into both arrays and increment only the smaller key each time:
int i = 0,j = 0;
while (i < List1.size() && j < List2.size()){
int x = List1.get(i).getCmpKey().CompareTo(List2.get(j).getCmpKey();
if (x == 0){
//add record
j++;
}else if(x < 0){
i++;
}else{
j++;
}
}
If your equals (and hashCode) implementation are based on getCmpkey() you can use Sets.
set1 = new HashSet(list1)
set2 = new HashSet(list2)
set2.retainAll(set1);
for(AccVO a : set2) //insert
This will have O(1) for individual removes (O(n) for n elements in set1).
I would suggest using a HashMap for the second list.
If the type of the Lists is not specified you should use Iterators to traverse them. This will give you guaranteed O(n) (linear) performance even if you use a List implementation that's not backed by an Array (assuming it you can iterate in O(n) time).
For example, if you happened to be given a LinkedList as your list class, the following implementation is still O(n) but an implementation that used get() to index into the list would be O(n^2). So if you list sizes were each 1 million, this implementation would be 1 million times faster than an indexing implementation.
Iterator i1 = list1.iterator();
Iterator i2 = list2.iterator();
if (i1.hasNext() && i2.hasNext() {
AccVO v1 = i1.next();
AccVO v2 = i2.next();
while (true) {
int i = v1.getCmpKey().compareTo(v2.getCmpKey());
if (i == 0) {
// insert record into DB here
if (i2.hasNext()) {
v2 = i2.next()
} else {
break;
}
} else if (i < 0) {
if (i1.hasNext()) {
v1 = i1.next();
} else {
break;
}
} else {
if (i2.hasNext()) {
v2 = i2.next();
} else {
break;
}
}
}
}
I think I'd do something along the lines of
Set<Integer> goodKeys = new HashSet<Integer>();
for (AccVO a : List1)
goodKeys.add(a.getCmpkey());
for (AccVO a : List2)
if (goodKeys.contains(a.getCmpkey()))
// insert the recs into the table
I could then hang on to the list of good keys, if desired, extract the first chunk into getKeys(List<AccVO> list), extract the remainder into insertRecordsForKeys(Set<Integer> keys), etc. Easier to test, easier to debug, easier to reuse, easier to work with.
Related
I am traversing through two lists whose size does not match. One list size is greater than the other. Say List A has five objects in it and List B has two, then my code works for only two objects in List B avoiding three others in List A. I wanted to add some constant value in that list for that List A. Below is the code.
for (int i = 0; i < filteredStartDate.size(); i++) {
// Long sum = null;
if ((fsFFDB.size() != listHoldingsDBRecords.size())
&& (filteredStartDate.get(i)
.isBefore(filteredEndDate.get(i)))) {
try {
tosValues.add(fsFFDB.get(i).getFloatShares());
}catch (Exception e) {
e.printStackTrace();
tosValues.add(fsFFDB.get(fsFFDB.size()-1).getFloatShares());
}
} else if ((fsFFDB.size() == listHoldingsDBRecords.size())
&& (filteredStartDate.get(i).isBefore(filteredEndDate.get(i)))) {
tosValues.add(fsFFDB.get(i).getFloatShares());
}
}
Currently, I have a filteredStartDate list size which is 3 and the fsFFDB size list is 2. So it was straightforward, the last record will remain vacant and will throw Index out of bounds exception. I have caught the exception and then in the catch block, I have inserted the constant value which is fsFFDB.size()-1).getFloatShares(). But what I want is to say if filteredStartDate size is 5 and fsFFDB size is 1, then for the remaining four records also the constant value should be inserted. I want my code to be dynamic. Please help. Can we replace all other values in list with some constant?
To start off, using Exceptions for normal operation is not good practice, and it will be bad for performance. A better idea is to detect when you are out of elements in one list by comparing your current index against the length of the list. Here is an example:
List<Integer> a = Arrays.asList(1,2,3,4,5);
List<Integer> b = Arrays.asList(6,7,8);
List<Integer> c = new ArrayList<>();
int aConst = 9;
int bConst = 10;
for(int i = 0; i < a.size() || i < b.size(); i++) {
c.add(
(i < a.size() ? a.get(i) : aConst) +
(i < b.size() ? b.get(i) : bConst)
);
}
I am having difficulty mapping the data in two lists into a third list. My sample data is as follows:
Categorylist ID: 1,2,3,4,5,6,7,8,9,10,42,46,49,50
CurrentMonthByCat ID: 1,2,3,4,5,6,7,8,9,10,42,49,50
(the transaction amount value for CurrentMonthByCat: 92,46,40,180,60,10,1510,200,500,10,234,12)
There is a missing 46 in the currentMonthByCat. I am trying to do it in a way such that if the currentMonthByCat ID does not exist from the categorylist ID, I will insert 0 into the third list rather than getting the transaction amount from CurrentMonthByCat and push it into the third list.
ArrayList<Double> total = new ArrayList<Double>();
for(int i = 0; i < categorylist.size(); i++){
for(int j = 0; j < currentMonthByCat.size(); j++){
if(categorylist.get(i).getCategoryID().equals(currentMonthByCat.get(j).getCategory().getCategoryID())){
Log.d("IIIII", categorylist.get(i).getCategoryID());
Log.d("JJJJJ", currentMonthByCat.get(j).getCategory().getCategoryID());
total.add((double)currentMonthByCat.get(j).getTransactionAmt());
}else{
total.add(0.0);
}
}
}
for(int k = 0; k < total.size(); k++){
Log.d("KKKKK", String.valueOf(total.get(k)));
}
But the printed out result of total list is:
92,0,0,0,0,0,0,0,0,0,0,0,0,0,46,0,0,0...
What I expected is:
92,46,40,180,60,10,1510,200,500,10,0,234,12
I wanted to insert 0 only if the ID in currentMonthByCat does not match the ID in categorylist. For instance, the ID 46 which is the 3rd position from the right.
I realized the reason is because firstly I inserted 92 into third array, then the categorylist ID is still at 1, then it will compare with all the rest in the currentMonthByCat before moving to ID 2. That is why the unnecessary zeros. But I not sure how to actually sort it to achieve what I wanted.
Any ideas?
Thanks in advance.
It's easy one. you can't take a decision of adding zero or value in the total array unless the inner loop finish. so probably you add element existAtIndex and initialize it with -1 and in the loop if you find the element then assign the index to existAtIndex and break the loop or if it's not exist then you add zero. so the code will be something like :
ArrayList<Double> total = new ArrayList<Double>();
int existAtIndex;
for(int i = 0; i < categorylist.size(); i++){
// search for the element index
existAtIndex = -1;
for(int j = 0; j < currentMonthByCat.size(); j++){
if(categorylist.get(i).getCategoryID().equals(currentMonthByCat.get(j).getCategory().getCategoryID())){
existAtIndex = j;
break;
}
}
// add the value in the element index or add zero if the element not exist
if (existAtIndex != -1) {
total.add((double)currentMonthByCat.get(existAtIndex).getTransactionAmt());
}
else {
total.add(0.0);
}
}
for(int k = 0; k < total.size(); k++){
Log.d(String.valueOf(total.get(k)));
}
For better code you could use contains method to check for the item if exist or not in arrayList instead of using the basic loop. Good luck
You have a lot of code for what you are trying to do here. I think the following snippet does what you want in a very readable and maintainable way.
//First of all we are interested in getting a transaction amount for each value in currentMonthByCat
//so loop around using your object (not sure what it's called)
for(CurrentMonth value : currentMonthByCat){
//check if it's present.
//We create a new method here that gets you your category list as a list of integers.
//This is key to making the whole method much more readable.
if(categorylist.getIdsAsList().contains(value.getCategory().getCategoryID())){
//it is so add it
total.add(value.getTransactionAmt());
} else {
//it's not so add a 0
total.add(0.0);
}
}
The getIdsAsList method could look like this:
public List<Integer> getIdsAsList(){
List<Integer> result = new ArrayList<>();
for (CategoryListItem item : categorylist) {
result.add(item.getCategoryId());
}
return result;
}
Put your values into Map<Integer, Double>
Map<Integer, Double> map = new HashMap<Integer, Double>();
for (int i = 0; i < currentMonthByCat.size(); ++i) {
//... categoryId = currentMonthByCat.get(i).categoryId
//... amount = currentMonthByCat.get(i).amount
map.put(categoryId, amount);
}
Then traverse the map using values from Categorylist ID:
// create result arraylist
ArrayList<Double> total = new ArrayList<Double>();
for (int i = 0; i < categorylist.size(); ++i) {
Double amount = map.get(categorylist.get(i));
if (amount == null) {
total.add(0.0);
} else {
total.add(amount);
}
}
The result list total will contain amounts for existing mappings, or zeros for non-existent.
Other way
If it is guaranteed categorylist is sorted and CurrentMonthByCat is sorted
you can then traverse one of the lists while keeping index/cursor to the other list and not iterating the other list from the beginning, but from previously remembered cursor value, resulting in better average performance than n^2
I was wondering what could be a better solution that could produce less complexity than O(n^2) when printing unique items from two arrays. Any ideas?
int[] a = {1,2,4,5,8};
int[] b = {3,2,5,7,8};
ArrayList unMatch = new ArrayList() ;
for(int i=0; i<a.length; i++){
boolean contains = false;
innerloop:
for(int k =0; k<b.length; k++){
if(a[i]==b[k]){
contains = true;
break innerloop;
}
}
if(!contains){
unMatch.add(a[i]);
}
}
for(int i=0; i<b.length; i++){
boolean contains = false;
innerloop:
for(int k =0; k<a.length; k++){
if(b[i]==a[k]){
contains = true;
break innerloop;
}
}
if(!contains){
unMatch.add(b[i]);
}
}
Output: [1,4,3,7]
I think this sort of solution will be better, if you can use other data structures:
First we will fill up a HashMap<Integer, Integer> with the items and their frequencies:
public static Set<Entry<Integer, Integer>> fillMap(int[] a, int[] b) {
HashMap<Integer, Integer> entries = new HashMap<>();
for (Integer i : a)
entries.put(i, entries.get(i) == null ? 1 : entries.get(i) + 1);
for (Integer i : b)
entries.put(i, entries.get(i) == null ? 1 : entries.get(i) + 1);
return entries.entrySet();
}
And then print the unique items (the ones with value = 1):
for (Entry<Integer, Integer> entry: fillMap(a, b))
if (entry.getValue() == 1)
System.out.println("This value is unique: " + entry.getKey() );
If I'm not mistaken this should run in O(n+m) (or just O(n) if the arrays are the same length always).
convert array to array list
List<Integer> c = Array.asList(a);
List<Integer> d = Array.asList<b>;
c.removeAll(d);
c.addAll(d);
c.froEach(System.out::println);
I did this in java using lambdas it is only O(n)
Hope this code answers your question
Use of Set can reduce your complexity. Sets don't allow duplicates. Sets can be:
HashSet - HashSet is implemented using a hash table. Elements are not ordered. The add, remove, and contains methods have constant time complexity O(1).
LinkedHashSet - uses Trees (RB-Trees). Elements are not ordered. Complexity for the same methods is O(log n)
TreeSet - uses a hash table with a linked list running through it. Elements are ordered. The time complexity of the same methods is O(1).
E.g.
HashSet<Integer> set = new HashSet<>();
for(int n : a) {
set.add(n);
}
for (int n : b) {
set.add(n);
}
So, it provides a linear order here - O(n+m).
I know this might be simple, I have a situation where I need to decide between using four for-loops to (two to count and remove null elements, two to add elements) merge two String arrays or use two for-loops with an ArrayList and convert the ArrayList to array using ArrayList.toArray().
Performance wise are there any differences between these two approaches?
EDIT
I had to drop the ArrayList with generics approach because of compatibility issues. But here is the earlier code.
List<String> newList = new ArrayList<String>();
for (String element : array1)
{
if (element != null)
{
newList.add(element);
}
}
for (String element : array2)
{
if (element != null)
{
newList.add(element);
}
}
return newList.toArray(new String[]{});
I wrote a new code with one loop, but I think I might be mentally killing the next one reading this code.
String[] newArr = new String[array1.length + array2.length];
int n = 0;
for (int i = 0; i < newArr.length; i++)
{
if (i < array1.length && array1[i] != null)
{
newArr[n] = array1[i];
n++;
}
if (i >= array1.length)
{
int a = 0;
if (array1.length < array2.length)
{
a = (i - array1.length) + (array2.length - array1.length);
}
else
{
a = i - array1.length;
}
if (array2[a] != null)
{
newArr[n] = array2[a];
n++;
}
}
}
return newArr;
And finally got to know that null element check won't be needed so went ahead with this simple code.
String[] newArr = new String[array1.length + array2.length];
System.arraycopy(array1, 0, newArr, 0, array1.length);
System.arraycopy(array2, 0, newArr, array1.length, array2.length);
return newArr;
I guess from the discussion below the second method is the better performing one.
Assuming 1 for loop has O(n) time complexity, 4 for loops and 2 for loops would have the same time complexity
4*O(n) = O(n)
2*O(n) = O(n)
But using arrays instead of ArrayLists would take less memory. So go with the first alternative.
Try to avoid looping as much as you can because every time u loop with n elements the time and space complexity increases by n.
Please paste your code.
as with second option 2 iterations over collection will be saved, this will definitely improve performance if count of objects in list is high.
I have an array containing duplicates in the following format:
arr[]={ 2,9,1,5,1,4,9,7,2,1,4 }
I want to sort the array in place such that all the duplicate elements are moved towards the end and sorted in different sub arrays like following:
arr[]={ 1,2,4,5,7,9, 1,2,4,9, 1 }
There is no range for Integers for the array specified.
Following is the code which i tried. This code recursively sorts the sub-arrays and then move duplicates towards the end. But Complexity wise this is not optimal solution.
Please suggest if it can be solve in O(n) or O(nlogn). Entire code is as follows:
public static int sortDuplicates(int a[],int start,int end){
int i, k,temp;
if(start==end)
return 1;
Arrays.sort(a, start, end);
k = start;
for (i = start+1; i < end; i++) {
if (a[k] != a[i] && a[k]<a[i])
{
temp=a[k+1];
a[k+1] = a[i];
a[i]=temp;
k++;
}
}
return sortDuplicates(a,k+1,a.length);
}
I would approach it as follows:
Put all elements and their counts in a hashmap:
int[] arr={ 2,9,1,5,1,4,9,7,2,1,4 };
Map<Integer, Integer> map = new HashMap<>();
for (int elt : arr) {
int val = 1;
if (map.containsKey(elt))
val = map.get(elt) + 1;
map.put(elt, val);
}
Fetch the batches one by one:
List<Integer> result = new ArrayList<>();
while (map.size() > 0) {
List<Integer> subList = new ArrayList<>(); // for the current segment
Iterator<Integer> it = map.keySet().iterator();
while (it.hasNext()) {
int elt = it.next();
subList.add(elt);
if (map.get(elt) == 1) { // remove all elements with occurence = 1
it.remove();
} else { // reduce occurence by 1
map.put(elt, map.get(elt)-1);
}
}
Collections.sort(subList); // sort this segment
result.addAll(subList); // append to result
}
for (int i : result) {
System.out.print(i + " ");
}
This prints
1 2 4 5 7 9 1 2 4 9 1
And if I'm not mistaken, it runs in O(n log n).
I would approach it as follows.
Put all the elements in a balanced binary search tree with their counts.
Now traverse the tree in an inorder way and keep putting the elements(with count > 0) in the array and decreasing their count by 1.
time complexity for creating the tree is O(nlgn) and for putting elements from tree is O(n) and space complexity is O(n).
Problem is if none of elements is being repeated then we are creating the tree in vain.