I have a array list contains thousands of data.
For Example:
List<String> custNames = new ArrayList<String>();
custNames.add("John");
custNames.add("Tom");
custNames.add("Bart");
custNames.add("Tim");
custNames.add("Broad");
Now I want to get count of names only starting with 'T'. I used looping mechanism for my solution.
List<String> filterNames = new ArrayList<String>();
String nameStarts="T";
for(int i=0;i<custNames.size();i++)
{
if(custNames.get(i).toLowerCase().startsWith(nameStarts.toLowerCase()))
{
filterNames.add(custNames.get(i));
}
}
System.out.println(filterNames.size());
But I have very large collection of data in this custNames list.
Is there any different solution without using loop?
Thanks.
There is very good solution from Java 8 for your problem.
Try this,
long filterNameCount = custNames
.stream()
.parallel()
.filter((s) -> s.startsWith(nameStarts.toLowerCase()))
.count();
System.out.println(filterNameCount);
If you are open to using a third-party library, there are a few interesting options you could use with Eclipse Collections.
If you use the ArrayList as you have it above, you can use the LazyIterate utility as follows:
int count = LazyIterate.collect(custNames, String::toLowerCase)
.countWith(String::startsWith, nameStarts.toLowerCase());
Assert.assertEquals(2, count);
If you use the Eclipse Collections replacement for ArrayList, you can use the rich functional protocols available directly on MutableList:
MutableList<String> custNames =
Lists.mutable.with("John", "Tom", "Bart", "Tim", "Broad");
String nameStarts= "T";
int count = custNames.asLazy()
.collect(String::toLowerCase)
.countWith(String::startsWith, nameStarts.toLowerCase());
System.out.println(count);
Assert.assertEquals(2, count);
The serial API in Eclipse Collections is eager-by-default, which is why I called asLazy() first. The collect method would otherwise create another MutableList.
If you benchmark your code with your full set of data, the following parallel version of the code may be more performant:
MutableList<String> custNames =
Lists.mutable.with("John", "Tom", "Bart", "Tim", "Broad");
String nameStarts= "T";
int processors = Runtime.getRuntime().availableProcessors();
int batchSize = Math.max(1, custNames.size() / processors);
ExecutorService executor = Executors.newFixedThreadPool(processors);
int count = custNames.asParallel(executor, batchSize)
.collect(String::toLowerCase)
.countWith(String::startsWith, nameStarts.toLowerCase());
executor.shutdown();
Assert.assertEquals(2, count);
The asParallel() API in Eclipse Collections is lazy-by-default. The API forces you to pass in a an ExecutorService and an int batchSize. This gives you complete control over the parallelism.
You can also use the Stream API with all MutableCollections in Eclipse Collections because they extend java.util.Collection.
Note: I am a committer for Eclipse Collections.
You could also use a tree storage : it would very efficient for this kind of search. If you are stucked with a list the previous answered is a way to do.
remove all the items which dont start with "T" like this:
custNames.removeIf(p->!p.startsWith("T"));
you can make a copy out of your list and remove items not starting with "T".
First, you can shorten your initialization with Arrays.asList(T); Second, I would use a simple loop to build a table of counts once and then use that to determine the subsequent queries. Something like,
List<String> custNames = new ArrayList<String>(Arrays.asList("John", "Tom",
"Bart", "Tim", "Broad"));
int[] counts = new int[26];
for (String name : custNames) {
char ch = Character.toLowerCase(name.charAt(0));
counts[ch - 'a']++;
}
for (int i = 0; i < counts.length; i++) {
if (counts[i] > 0) {
System.out.printf("There are %d words that start with %c%n",
counts[i], (char) ('a' + i));
}
}
Which outputs
There are 2 words that start with b
There are 1 words that start with j
There are 2 words that start with t
Or, in the specific case - counts['t' - 'a'] is the count of words starting with t.
If you have more or less static list and perform search operation often you can sort your list or use TreeMap.
Also you don't need to create new list and get its size then. You can simply create a counter variable and increment it.
You can create your own sorting and finding implementation.
Consider the following:
public class ContainingArrayList<E> extends ArrayList<E> {
private Comparator<E> comparator;
public ContainingArrayList(Comparator<E> comparator) {
this.setComparator(comparator);
}
#Override
public boolean add(E e) {
// If the collection is empty or the new element is bigger than the last one, append it to the end of the collection
if(size() == 0 || comparator.compare(e, get(size()-1)) >= 0)
return super.add(e);
else {
for (int i = 0; i < size(); i++) {
int result = comparator.compare(e, get(i));
// If the new element is bigger than the current element, continue with the next element
if (result > 0) continue;
// If the new element is equal to the current element, no need to insert (you might insert of course)
if (result == 0) return false;
// Otherwise the new element is smaller than the current element, so insert it between the previous and the current element
super.add(i, e);
return true;
}
return super.add(e);
}
}
public E get(E containingElement) {
int start = 0;
int end = size()-1;
// If the element is the first one, return the first element
if(comparator.compare(containingElement, super.get(start)) == 0)
return super.get(start);
// If the element is the last one, return the last element
if(comparator.compare(containingElement, super.get(end)) == 0)
return super.get(end);
// Otherwise do a binary search
while(start != end) {
// Get the element between start and end positions
E mid = super.get(start + (end/2));
// Compare the two elements
int result = comparator.compare(containingElement, mid);
// If the middle element compared to the containing element is equal, return the middle element
if(result == 0) {
return mid;
}
// If the containing element is smaller than the middle, halve the end position
else if(result < 0) {
end = start + (end/2);
}
// If the containing element is bigger than the middle, set the start position to the middle position
else if(result > 0) {
start = start + (end/2);
}
}
return null;
}
public Comparator<E> getComparator() {
return comparator;
}
public void setComparator(Comparator<E> comparator) {
this.comparator = comparator;
}
}
The custom comparator is used to sort the elements and to find the element that starts with a specific character. This means that you can change the comparator implementation for your needs at any time or you can create a more dynamic finding solution.
Test:
public class SortFindTest {
public SortFindTest() {
ContainingArrayList<String> t = new ContainingArrayList<String>(new MyComparator());
t.add("John");
t.add("Tom");
t.add("Bart");
t.add("Tim");
t.add("Broad");
System.out.println(t.get("T"));
}
class MyComparator implements Comparator<String> {
#Override
public int compare(String o1, String o2) {
int o1c = o1.charAt(0);
int o2c = o2.charAt(0);
if(o1c == o2c)
return 0;
if(o1c > o2c)
return 1;
return -1;
}
}
public static void main(String[] args) {
new SortFindTest();
}
}
I'm not sure if this would be faster than Java 8 Stream API but it worth a try.
If the order in which the items are stored does not matter, you could store the names in a HashMap, where the first character of each name is the key, and an ArrayList of names with that first character are the values. And then all you need to do, assuming the HashMap is named customerList, is customerList.get("T").size().
Initializing HashList and Adding Customers
HashMap<Character, ArrayList<String>> customerList = new HashMap<Character, ArrayList<String>>();
int NUM_ALPHABETS = 26;
int ascii_char = 97;
for(int i = 0; i < NUM_ALPHABETS; i++){
char c = (char) ascii_char;
customerList.add(c, new ArrayList<String>());
ascii_char++;
}
customerList.get("t").add("Tony");
customerList.get("a").add("Alice");
customerList.get("b").add("Ben");
Getting Number of Customers Starting with "t"
int num_t = customerList.get("t").size();
Related
Solution{
String question
String answer
public Solution(String question,String answer){
...
}
}
List<Solution> solutions = new ArrayList<>();
Arrays.asList(
new Solution("Index1","a"),
new Solution("Index2","b"),
new Solution("Index3","c"),
new Solution("Index4","d"),
new Solution("Index5","ae"),
new Solution("Index1","afg"),
new Solution("Index2","adfg"),
new Solution("Index1","ag"),
new Solution("Index2","a"),
new Solution("Index3","a"),
new Solution("Index4","a"),
new Solution("Index5","a"),
new Solution("Index1","arrr"),
new Solution("Index2","a"),
new Solution("Index3","a"));
I always want to get last two sets starting from Index1 which are
new Solution("Index1","ag"),
new Solution("Index2","a"),
new Solution("Index3","a"),
new Solution("Index4","a"),
new Solution("Index5","a"),
new Solution("Index1","arrr"),
new Solution("Index2","a"),
new Solution("Index3","a"))
but I am not sure what is the best way to do that. I can think of reversing the list and then have a counter on Index1 which starts with 0
then do while loop to add it in a list until counter reaches 2. Not sure if this is possible with streams.
I think the simplest way is to get the positions at which Index1 occurs in the list of solutions. These represent potential start position of the sublist. You can do this by using an IntStream over the indexes into the solutions list. Then, take the second-to-last start point as the starting of a sublist that goes to the end of the list.
List<Integer> starts = IntStream.range(0, solutions.size())
.filter(i -> solutions.get(i).getQuestion().equals("Index1"))
.boxed()
.collect(toList());
if (starts.size() < 2) {
// not sure what you want to do in this case
} else {
List<Solution> lastTwoSets = solutions.subList(starts.get(starts.size()-2), solutions.size());
lastTwoSets.forEach(System.out::println);
}
It occurs to me that using an int[] instead of List<Integer> makes things slightly more efficient as well as a bit more concise. The techique is otherwise essentially the same.
int[] starts = IntStream.range(0, solutions.size())
.filter(i -> solutions.get(i).question.equals("Index1"))
.toArray();
if (starts.length < 2) {
// not sure what you want to do in this case
} else {
List<Solution> lastTwoSets = solutions.subList(starts[starts.length-2], solutions.size());
lastTwoSets.forEach(System.out::println);
}
This is quite a bit simpler with a normal for-loop than with streams. You can loop through the list and keep track of the indexes of the last two occurrences of Index1. When you know the second to last index, you can use the subList method to get your final list.
To make it faster, you can iterate backwards from the end and find the first two occurrences. You can then stop when you hit Index1 the second time.
This example iterates through from the beginning:
int firstIndex = -1;
int secondIndex = -1;
for (int i = 0; i < solutions.size(); i++) {
if (solutions.get(i).getQuestion().equals("Index1")) {
firstIndex = secondIndex;
secondIndex = i;
}
}
if (firstIndex == -1) {
// There weren't two occurrences of "Index1", so I return the whole list.
return solutions;
}
return solutions.subList(firstIndex, solutions.size());
Note that the subList method returns a view over your original list. This means that it won't iterate over your list a second time when you call it.
It also means that if you mutate your original list, the changes will be reflected in the sublist.
This solution is a bit tricky because the filtering criteria is multi-part, but this can be accomplished by counting the number of times that a question index has been seen, at most adding only 2 solutions per question, and stopping once Index1 has been seen twice.
The filtering object for this criteria would be:
public class SolutionFilter implements Predicate<Solution> {
private final Map<String, Integer> counter = new HashMap<>();
#Override
public boolean test(Solution solution) {
Integer index1Count = counter.get("Index1");
if (index1Count != null && index1Count == 2) {
return false;
}
Integer count = counter.get(solution.getQuestion());
if (count == null) {
counter.put(solution.getQuestion(), 1);
return true;
}
else if (count == 1) {
counter.put(solution.getQuestion(), 2);
return true;
}
else {
return false;
}
}
}
To ensure that the solutions are added in reverse order, a loop is performed, starting at the end and progressing towards the beginning of the solutions list. Likewise, to ensure that the output list is not in reverse order, a Deque is used and the matching Solutions are added to the head of the Deque:
public static void main(final String[] args) {
List<Solution> solutions = Arrays.asList(
new Solution("Index1","a"),
new Solution("Index2","b"),
new Solution("Index3","c"),
new Solution("Index4","d"),
new Solution("Index5","ae"),
new Solution("Index1","afg"),
new Solution("Index2","adfg"),
new Solution("Index1","ag"),
new Solution("Index2","a"),
new Solution("Index3","a"),
new Solution("Index4","a"),
new Solution("Index5","a"),
new Solution("Index1","arrr"),
new Solution("Index2","a"),
new Solution("Index3","a")
);
SolutionFilter filter = new SolutionFilter();
Deque<Solution> filteredSolutions = new LinkedList<>();
for (int i = solutions.size() - 1; i > 0; i--) {
Solution solution = solutions.get(i);
if (filter.test(solution)) {
filteredSolutions.addFirst(solution);
}
}
System.out.println(filteredSolutions);
}
This results in the following output:
[{Index1: ag}, {Index2: a}, {Index3: a}, {Index4: a}, {Index5: a}, {Index1: arrr}, {Index2: a}, {Index3: a}]
This can be accomplished using a Stream, but it may be more complicated.
I'm trying to write a method that returns a sub lists and each containin the max value of strigns from a larger list.
I'm using iterator and backtrack recursion to achieve this, however - I'm not familiar with backtrack recursion. Any suggestions on how to make this work. Or if my code needs adjustments?
This is what I have so far:
private void printAnagrams(List<String> anagrams, int max, List<List<String>> listofLists) {
Iterator<String> iterate = anagrams.iterator();
String word = "";
while (iterate.hasNext()) {
for(int i = 0; i < anagrams.size(); i++) {
word = iterate.next();
listofLists.add(new ArrayList<>());
listofLists.get(i).add(word);
if (listofLists.size() == max) {
listofLists.add(new ArrayList<>());
// Continue new list
}
}
}
}
This is the method that calls the private method:
public void printAnagrams(String phrase, int max, List<List<String>> anagrams) {
if (anagrams == null || max < 0) {
throw new IllegalArgumentException();
} else if (max == 0) {
getWords(phrase);
} else
printAnagrams(phrase, max, anagrams);
}// End of printAnagram method
Sample output:
List:
[core, course, cs, cure, for, force, forces, four, of, off, offer, offers, or, our, ours, re, score, so, source, suffer, sure, us, use, user]
SubLists with max = 3:
[core, off, us]
[core, us, off]
[course, off]
[cure, off, so]
[cure, so, off]
[force, of, us]
[force, us, of]
I am stuck.
The following function is supposed to return currVm, an integer. But if I make a return I will break the loop and next time when this function is called,the same process will begin again.
What shall I do, so that I continue from where I left off ? I tried making static variables but I that didn't help me.
#Override
public int getNextAvailableVm() {
Set<String> dataCenters = confMap.keySet();
for (String dataCenter : dataCenters) {
LinkedList<DepConfAttr> list = confMap.get(dataCenter);
Collections.sort(list, new MemoryComparator());
int size = list.size() - 1;
int count = 0;
while(size >= 0) {
DepConfAttr dca = (DepConfAttr)list.get(count);
int currVm = dca.getVmCount();
int c = 0;
while(c <= currVm) {
allocatedVm(currVm);
c++;
return currVm;
}
count++;
size--;
}
}
return 0;
}
The for-each loop assigns a new data center that acts as a key for the confMap.The list that I get as a value, is sorted.Then a loop is run till it escapes its size.Inside this while loop, another while loop is run from where a function named allocatedVm of the inherited class is called. A parameter named currVm is passed to it.
This is the variable that I need to return. What shall I do to return this variable ? I have to start from I left off. I mean the next call should appear to be the next step, whatever it was, while executing the loop.
Add List<Integer> object to your class, and change your method as follows:
private Iterator<Integer> availableVms = null;
#Override
public int getNextAvailableVm() {
if (availableVms != null) {
if (availableVms.hasNext()) {
return availableVms.next();
}
return 0;
}
List<Integer> tmp = new ArrayList<Integer>();
Set<String> dataCenters = confMap.keySet();
for (String dataCenter : dataCenters) {
LinkedList<DepConfAttr> list = confMap.get(dataCenter);
Collections.sort(list, new MemoryComparator());
int size = list.size() - 1;
int count = 0;
while(size >= 0) {
DepConfAttr dca = (DepConfAttr)list.get(count);
int currVm = dca.getVmCount();
int c = 0;
while(c <= currVm) {
allocatedVm(currVm);
c++;
tmp.add(currVm);
}
count++;
size--;
}
}
availableVms = tmp.iterator();
return availableVms.hasNext() ? availableVms.next() : 0;
}
The idea is to pre-generate the entire list, and store its iterator for future use. Before entering the method you check if the availableVms iterator has been prepared. If it has been prepared, grab the next item off of it if it's available; otherwise, return zero.
If the list has not been prepared yet, run your algorithm, and add the results to a temporary list tmp. Once the list is ready, grab its iterator, and use it for subsequent invocations.
I have two arraylist name preBusinessList, businessList. In business List I have data from server, and in preBusinessList is the local one. In lists I have id, count value Betterly demonstrate as below
Now I wanted to make a newBusinessList like this
How can I do it in java, please help me to solve this
Then I would use a map to do the merge using id as the key and convert it back to your list of (id,value) pairs
You can use:
Collections.sort(new ArrayList<...>(preBusinessList).addAll(businessList), comparator)
Where comparator is a class that implements Comparator interface (will be responsible for sorting as you wish)
assumming i understood your problem correctly (big if...):
also, i assume each element in the lists is a Pair - as it looks from your data (just a dumb wrapper class that holds 2 integers). if its some other class you'll need to adjust this code.
private Map<Integer,Integer> finalValues = new HashMap<Integer,Integer>();
for (Pair<Integer,Integer> entry : preBusinessList) {
finalValues.put(entry.getFirst(), entry.getSecond());
}
//2nd list overwrites values from 1st (anything not overwritten remains)
for (Pair<Integer,Integer> entry : businessList) {
finalValues.put(entry.getFirst(), entry.getSecond());
}
ArrayList<Pair<Integer,Integer>> finalList = new ArrayList<>();
for (Map.Entry<Integer,Integer> entry : finalValues) {
finalList.add(new Pair(entry.getKey(), entry.getValue());
}
//and now sort the list
Collections.sort(finalList, new Comparator<Pair<Integer,Integer>> {
int compare(Pair<Integer,Integer> a, Pair<Integer,Integer>b) {
return a.getFirst.compareTo(b.getFirst()); //compare by 1st number in pair only
}
});
Assuming something like:
public class Info {
public int id;
public int info;
}
You could merge them on the basis of wanting the keep the one with higher info field as follows:
// Assumes:
// - that the ArrayLists are sorted to have id in order going up
// - no repeated ids in a or in b (but same id can be in both a and b)
ArrayList<Info> merge(ArrayList<Info> a, ArrayList<Info> b) {
int aLength = a.size();
int bLength = b.size();
int ai = 0;
int bi = 0;
ArrayList<Info> result = new ArrayList<Info>();
while ((ai < aLength) && (bi < bLength))
Info aInfo = a.get(ai);
Info bInfo = b.get(bi);
if (aInfo.id == bInfo.id) {
if (aInfo.info >= bInfo.info) result.add(aInfo);
else result.add(bInfo);
ai++;
bi++;
}
else if (aInfo.id < bInfo.id) {
result.add(aInfo);
ai++;
}
else {
result.add(bInfo);
bi++;
}
}
// Add the remaining terms - only one of the loops will actually do anything
for (; ai<aiLength; ai++) {
result.add(a.get(ai));
}
for (; bi<biLength; bi++) {
result.add(b.get(bi));
}
}
Pseudocode :
Iterate over preBusinessList.
Fetch key and see if this key(1,2,3,4,5,6) exists in businesslist
If yes conitnue
Else If no, then add it to businesslist
for(Map.Entry<Integer, Integer> keyValue : preBusinessList.entrySet()) {
if(!businesslist.containsKey(keyValue.getKey())) {
businesslist.put(keyValue.getKey(), keyValue.getValue());
}
}
Updated Answer as per new requirements
boolean ifExists = false;
for(PlaceItems itemPreBusinessList : preBusinessList) {
ifExists = false;
for(PlaceItems itemBusinessList : businessList) {
if(itemBusinessList.businessId == itemPreBusinessList.businessId) {
// Already exists
ifExists = true;
break;
}
}
if(!isExists) {
businessList.add(itemPreBusinessList);
}
}
I'm using an ArrayList to hold a history of objects. Each new object I add using the .add method, like:
if(event.getAction() == MotionEvent.ACTION_UP)
{
if(currentWord != null)
{
wordHist.add(currentWord);
}
if(wordHist.size() > WORDHIST_MAX_COUNT)
{
wordHist.remove(0);
}
}
However I don't want this to grow indefinitely, but to be limited to a certain value. If it reaches this maximum value, I want the oldest object (index 0) to be removed, and the rest to be left shifted, so previous index 1 is now index 0, etc.
How can this be done?
Thanks
ArrayList is not really a good choice in this case, but it can by done by calling remove(0) method. But if you want to do that efficiently, a linked list is better
(edited to make it clear that LinkedList is not generally better than ArrayList, but only in this case)
If it reaches this maximum value, I want the oldest object (index 0) to be removed
Then do wordHist.remove(0). That will remove the element at index 0.
To be precise:
wordHist.add(new Word("hello"));
if (wordHist.size() > MAX_SIZE)
wordHist.remove(0);
As user658991 states however, you should be aware of that this is a linear operation, i.e., takes time proportional to the number of elements in the list.
You could do this in constant time using LinkedList methods add and removeFirst.
Another option would be to wrap an array, or ArrayList in a class called something like CircularArrayList. In circular list structures you'll override the oldest element when adding a new one.
Edit:
Your code works fine:
import java.util.*;
class Test {
static int WORDHIST_MAX_COUNT = 3;
static List<String> wordHist = new ArrayList<String>();
public static void add(String currentWord) {
// VERBATIM COPY OF YOUR CODE
if (true/*event.getAction() == MotionEvent.ACTION_UP*/)
{
if(currentWord != null)
{
wordHist.add(currentWord);
}
if(wordHist.size() > WORDHIST_MAX_COUNT)
{
wordHist.remove(0);
}
}
}
public static void main(String[] args) {
add("a");
add("b");
add("c");
for (int i = 0; i < wordHist.size(); i++)
System.out.printf("i: %d, word: %s%n", i, wordHist.get(i));
System.out.println();
add("d");
for (int i = 0; i < wordHist.size(); i++)
System.out.printf("i: %d, word: %s%n", i, wordHist.get(i));
}
}
Prints:
i: 0, word: a
i: 1, word: b
i: 2, word: c
i: 0, word: b <-- b is now at index 0.
i: 1, word: c
i: 2, word: d
Use the remove( ) method.
Using remove(0) will remove the element from the 0th index.
U can use list.remove(index)// here index being '0', this internally shifts rest of the array up. An alternative solution wud be to use a queue or dequeue.
One simple implementation of what Op De Cirkel suggested
import java.util.ArrayList;
import java.util.List;
public class SimpleCircularHistory {
private int sizeLimit, start = 0, end = 0;
boolean empty = false;
private List<String> history;
public SimpleCircularHistory(int sizeLimit) {
this.sizeLimit = sizeLimit;
history = new ArrayList<String>(sizeLimit);
}
public void add(String state){
empty = false;
end = (end + 1) % sizeLimit;
if(history.size() < sizeLimit){
history.add(state);
}else {
history.set(end, state);
start = (end + 1) % sizeLimit;
}
}
public String rollBack(){
if(empty){ // Empty
return null;
}else {
String state = history.get(end);
if(start == end){
empty = true;
}else {
end = (end + sizeLimit - 1) % sizeLimit;
}
return state;
}
}
public void print(){
if(empty){
System.out.println("Empty");
}else {
for(int i = start;; i = (i + 1) % sizeLimit){
System.out.println(history.get(i));
if(i == end) break;
}
System.out.println();
}
}
public static void main(String[] args) {
SimpleCircularHistory h = new SimpleCircularHistory(3);
h.add("a");
h.add("b");
h.add("c");
h.add("d");
h.add("e");
h.add("f");
h.print();
h.add("X");
h.add("Y");
h.rollBack();
h.rollBack();
h.print();
h.add("t");
h.add("v");
h.add("w");
h.print();
h.rollBack();
h.rollBack();
h.rollBack();
h.print();
h.rollBack();
h.print();
}
}
This would print out :
d
e
f
f
t
v
w
Empty
Empty
Yeah, I've noticed this behaviour in adroid's lists too. It's REALLY irritating.
Anyway, there is a way to get around it if I don't mind object creation/destruction and the resulting garbage collection (NEVER do this in a onDraw of a surfaceview or something).
What I do is basically have two tracking int's; one to place the new object, and one to remove it:
int trackInt = 0;
int removeInt = 0;
//and then, in the method/class you use this:
Object newobject = new Object();
//add to list
objectList.add(trackInt, newobject);
trackInt++;
if (bugList.size() > 20) //20 is the max number of object you want, ie the maximum size of the list
{
objectList.remove(removeInt);
trackInt = removeInt;
removeInt++;
if (removeInt > 19) //remember, the list is zero indexed!
{
removeInt = 0;
}
}
Commons-collections has exactly what you're looking for:
http://commons.apache.org/collections/apidocs/org/apache/commons/collections/buffer/CircularFifoBuffer.html