I have a java list of URLs. I wish to call a function for each URL in the list. And this function adds URL to the list.
Is it possible to loop over all the URLs including newly added URLs in the list?
for(String links: urls) {
ar = getNews(links);
}
inside getNews() there is:
urls.add(json.optString("next"));
I did this successfully using recursion. By calling:
getNews(json.optString("next"));
inside getNews()
You can use regular for loop and change the condition each iteration
int size = 1;
for (int i = 0 ; i < size ; ++i) {
ar = getNews(urls.get(i));
size = urls.size();
}
Use a Queue instead of a list:
Queue<String> q = new LinkedList<>();
q.add(initial urls);
while (!q.isEmpty()) {
String url = q.pop();
q.addAll(readNews(url));
}
I am assuming that this code lives outside the readNews method, i.e. there is no recursion here.
Also, since I assume this is some sort of crawler, you might want to keep track of URLs you visited previously, to avoid visiting them again:
Queue<String> q = new LinkedList<>();
Set<String> visited = new LinkedHashSet<>();
q.add(initial urls);
while (!q.isEmpty()) {
String url = q.pop();
if (visited.add(url)) {
q.addAll(readNews(url));
}
}
Related
I am trying to remove all elements of an ArrayList between a start and an endtag.
My list and my tags:
String startTag = "<p>";
String endTag = "</p>";
List<String> elements = new ArrayList<>();
Let's say my list looks like this:
[<text>, <p>, <text>, clean me, </text>, </p>, </text>]
I only want to delete the contents between the sepcified tags and the tags themselves.
This is my code for doing that:
boolean delete = false;
List<String> remove = new ArrayList<>();
for(String element : elements) {
if(delete) {
remove.add(element);
}
if(element.startsWith(startTag)) {
delete = true;
remove.add(element);
}
if(element.endsWith(endTag)) {
delete = false;
remove.add(element);
}
}
elements.removeAll(remove);
}
This is how my list "remove" looks like after that:
[<p>, <text>, clean me, </text>, </p>, </p>]
So after deleting those elements from my list it looks like this:
[]
When it should look like this:
[<text>, </text>]
How can I prevent Strings who have duplicates to be deleted when they are outside of the deletion range?
How can I prevent Strings who have duplicates to be deleted when they are outside of the deletion range?
By identifying the range to delete by element index instead of by element value. There are lots of ways you could do that, but here's one that I like:
List<String> remainingElements = elements;
List<String> result = new ArrayList<>();
for (int start = remainingElements.indexOf(startTag);
start >= 0;
start = remainingElements.indexOf(startTag)) {
List<String> tail = remainingElements.subList(start, remainingElements.size());
int end = tail.indexOf(endTag);
if (end >= 0) {
List<String> range = tail.subList(0, end + 1);
result.addAll(range);
range.clear();
remainingElements = tail;
} else {
break;
}
}
Note in particular that a subList is backed by its parent list, so that modifications to the former are reflected in the latter.
Note also that the details presented here follow the apparent idea of your original example: they match the first appearance of startTag with the first appearance after that of endTag. This might not be what you actually want if you need to account for tag nesting. For example, the result with startTag = "<text>"; endTag = "</text>"; would be [</p>, </text>]. You can still use subList in such a case, but you need to be cleverer about identifying the range boundaries.
Use a Iterator (that is concurration modification safe) and remove the elements instead of adding to a removelist
boolean delete = false;
Iterator it = elements.iterator();
while(it.hasNext()) {
String element it.next();
if(delete)
it.remove();
if(element.startsWith(startTag)) {
delete = true;
it.remove();
}
if(element.endsWith(endTag)) {
delete = false;
it.remove();
}
}
}
Not sure if the title makes sense. but I am currently stuck trying to get the last occurrence of an object in a list. Let's say the object is compromised of ages (integers) and names (strings). I'm trying to get the last occurrence of the age "18" within the list, and retrieve the name. How would I go about this?
Current block of code I'm working with (Sorry, had to water it down a lot):
private List<Callable<Void>> createTasks(ArrayList<NamesAndAges> namesAndAges) {
List<Callable<Void>> tasks = new ArrayList<Callable<Void>>();
int endOfIndex = 0, beginningOfIndex = 0;
for (NamesAndAges nameAndAge : namesAndAges)
{
if (endOfIndex >= minSize && endOfIndex == namesAndAges.lastIndexOf(???))
{
tasks.add(new addIntoDB(namesAndAges.subList(beginningOfIndex, endOfIndex)));
beginningofIndex = endOfIndex+1;
}
endOfIndex++;
}
return tasks;
}
I'm basically stuck on where the ??? are in the code. I thought I could go about it using lastIndexOf().
Based on this answer, if you have a list, you can start your iterator at the end of the collection and iterate backwards:
// Substitute appropriate type.
ArrayList<MyItem> a = new ArrayList<MyItem>();
// add your elements...
// Generate an iterator. Start after the last element.
ListIterator li = a.listIterator(a.size());
// Iterate in reverse.
MyItem mi = null;
while(li.hasPrevious()) {
mi = li.previous();
if(mi.getAge().equals("18")) { // obviously, your test will likely differ...
break;
}
}
Try something like this.
ArrayList<Integer> a = new ArrayList<Integer>();
// add your elements...
for(int i = 1; i < 19; i++)
{
a.add(i);
}
while(a.size() > 0)
{
System.out.println("last item: " + a.get(a.size() - 1));//print last item
a.remove(a.size() - 1);//remove last item
}
I need to get a list from another list so here is what I have done :
ArrayList<String> userList = user.getListSalarieByManager(login);
ArrayList<DHDemande> demandesList;
for (int i = 0; i < userList.size(); i++) {
demandesList = d.getDemandesForManager(userList.get(i));
}
Then I need to get the data from the list demandesList but I can't get this list outside the loop because this not have been initialized.
How can I get the data from the list inside the loop ?
That is because you haven't actually initialized your second list.
ArrayList<DHDemande> demandesList;
Should be:
ArrayList<DHDemande> demandesList = new ArrayList<DHDemande>();
By the way, the way your loop is set up sets the entire demandesList every iteration. Are you perhaps looking for List#add?
Edit: to answer you question in the comments:
Yes, you can add a list to another list using ArrayList#addAll - that would look like this:
ArrayList<String> userList = user.getListSalarieByManager(login);
ArrayList<DHDemande> demandesList = new ArrayList<DHDemande>();
for (int i = 0; i < userList.size(); i++) {
demandesList.addAll(d.getDemandesForManager(userList.get(i)));
}
Edit 2: just a small note, you can replace your for loop with a for-each, since you don't really need to know the value of i (index).
Example:
for (int i = 0; i < userList.size(); i++) {
demandesList.addAll(d.getDemandesForManager(userList.get(i)));
}
Turns into:
for (String user : userList) {
demandesList.addAll(d.getDemandesForManager(user));
}
You only need to initialize the list properly, inside or outside the loop,
but it appears that you want to add elements to the list inside the loop.
I changed your iteration over the loop to the modern java list iteration style.
// initialize variables just so this example compiles
UserProvider user = new UserProvider();
Object login = null;
DHDemandeProvider d = new DHDemandeProvider(); only
ArrayList<String> userList;
userList = user.getListSalarieByManager(login);
ArrayList<DHDemande> demandesList = new ArrayList<DHDemande>(); // construct list
for (String u: userList) {
demandesList.add(d.getDemandesForManager(u)); // add elements to list
}
I have a ArrayList that contains elements (fields are name and type). There are only two different possible types ("edu" and "ent") that I want each to be displayed in its own listview.
My idea was to create two new ArrayLists with same data and then loop through each and filter unwanted elements like this:
ListView listView_ent = (ListView) findViewById(R.id.popular_apps_list_ent);
ArrayList<DataHolder> data_ent = data;
for (int i = 0; i < data_ent.size(); i++) {
if(data_ent.get(i).getType().equals("edu")){
data_ent.remove(i);
}
}
listView_ent.setAdapter(new AppsAdapter(this, data_ent));
ListView listView_edu = (ListView) findViewById(R.id.popular_apps_list_edu);
ArrayList<DataHolder> data_edu = data;
for (int i = 0; i < data_edu.size(); i++) {
if(data_edu.get(i).getType().equals("ent")){
data_edu.remove(i);
}
}
listView_edu.setAdapter(new AppsAdapter(this, data_edu));
There are 10 elements in ArrayList, 5 of each type.
Problem is that at the end in the both listviews there are 4 same items displayed with mixed types.
Any idea what I did wrong?
1) copy the data
2) don't iterate using i and remove; use an iterator (remove method: http://docs.oracle.com/javase/7/docs/api/java/util/Iterator.html#remove()) or start at the end of the list
something like this:
ListView listView_ent = (ListView) findViewById(R.id.popular_apps_list_ent);
ArrayList<DataHolder> data_ent = new ArrayList( data);
for (int i = data_ent.size()-1; i >= 0; i--) {
if(data_ent.get(i).getType().equals("edu")){
data_ent.remove(i);
}
}
listView_ent.setAdapter(new AppsAdapter(this, data_ent));
ListView listView_edu = (ListView) findViewById(R.id.popular_apps_list_edu);
ArrayList<DataHolder> data_edu = = new ArrayList( data);
for (int i = data_edu.size()-1; i >= 0 ; i--) {
if(data_edu.get(i).getType().equals("ent")){
data_edu.remove(i);
}
}
listView_edu.setAdapter(new AppsAdapter(this, data_edu));
Yes, assignment will just copy the value of data_ent (which is a reference) to data_edu. They will both refer to the same object. So whatever changes you make in either list, same changes will reflect in the other list as well
This is you should do :-
List<Integer> data_edu = new ArrayList<Integer>(data_ent);
or use the addAll() function of array list.
Once you remove an item from your ArrayList, the indeces all shift down. You can either add in i-- after remove, or use an iterator:
Iterator<DataHolder> i = data_edu.iterator();
while (i.hasNext()) {
DataHolder d = i.next();
if (d.getType().equals(...) {
i.remove();
}
}
Rahul is correct regarding list references, but you have another problem as well.
ListView listView_ent = (ListView) findViewById(R.id.popular_apps_list_ent);
ArrayList<DataHolder> data_ent = data;
for (int i = 0; i < data_ent.size(); i++) {
if(data_ent.get(i).getType().equals("edu")){
data_ent.remove(i);
}
}
The problem is that when you remove, you bugger your indices. You're essentially skipping items. Consider
{"edu", "edu", "ent"}
Once you take out the first item (index 0), the second edu becomes the new index 0, but you move on and check index 1.
Try using a ListIterator http://docs.oracle.com/javase/6/docs/api/java/util/ListIterator.html
hint:
ListIterator<DataHolder> entDataIterator = data_ent.listIterator();
while(entDataIterator.hasNext(){
if(/*whatever*/){
entDataIterator.remove();
}
}
In your for for loops, you may be skipping items. Let's say your list is something like that:
list = {edu, edu, ent, ent, edu}
Your index variable will be i = 0. list[i] == "edu" then you remove it, but then your list becomes:
list = {edu, ent, ent, edu}
But your index variable gets incremented and is then equals to 1. and list[1] = "ent". As you undersand you are not processing the first element of the list. You skipped indices.
Hope this is clear.
If you have commons-collections available in your project, you may as well use the filter method on CollectionUtils:
CollectionUtils.filter(your_list, new Predicate() {
#Override
public boolean evaluate(Object obj) {
return !((DataHolder) obj).getType().equals("edu");
}
});
Your remove loop is wrong. You can use this: for (int i= data_end.size - 1, i >=0, i--)
Think about feature extension later? Use more than 2 types?
The solution is very simple. Your code a filter function first
public List<DataHolder> filterBy(List<DataHolder> list, String type) {
List<DataHolder> l = new ArrayList<>();
for ( DataHolder h : list) {
if (h.getType().equals(type)) {
l.add(h);
}
}
return l;
}
Use the filter function:
List<DataHolder> eduList = filterBy(data, "edu");
listView_edu.setAdapter(new AppsAdapter(this, eduList));
List<DataHolder> entList = filterBy(data, "ent");
listView_ent.setAdapter(new AppsAdapter(this, entList));
Consider using Guava library for filtering collections:
http://www.motta-droid.com/2013/12/collections-performance-tests-in.html
In example above, filter returns new filtered collection don't affecting source collection. Maybe it's not worth time to add a library for one small task and if collections don't contain much items, but it's a good tool to be familiar with.
I have an XML array that I access to pull a random question from. How would I go about making sure there is no duplicates pulled? My current code follows.
private void getQuestion() {
// TODO Auto-generated method stub
res = getResources();
qString = res.getStringArray(R.array.questions);
rQuestion = qString[rgenerator.nextInt(qString.length)];
tokens = new StringTokenizer(rQuestion, ":");
wordCount = tokens.countTokens();
sep = new String[wordCount];
wArray = 0;
while (tokens.hasMoreTokens()) {
sep[wArray] = tokens.nextToken();
wArray++;
}
}
Any help would be appreciated.
The Fisher-Yates shuffle is an algorithm that is more or less designed for this purpose.
You are better off putting that array of questions in a list and use Collections.shuffle(). After that, simply iterate through the list. More information can be found at this related answer.
This solution will cost some memory for duplicating the list, but remember that the strings themselves won't be copied, only the references to the questions are. For maximum performance, use a list with random access (ArrayList), or use that as a replacement for the array. If you don't theshuffle method will create one internally.
If you want a fast way of getting only unique values from an array this link has a very fast method. Below uses an ArrayList, but it will not be hard for you to convert from string array to an ArrayList - or just use ArrayLists instead.
e.g. new ArrayList(Arrays.asList(myArray));
In short you use a hashset to only get unique values using this method
public static ArrayList GetUniqueValues(Collection values)
{
return new ArrayList(new HashSet(values));
}
Then use it like so
ArrayList x = new ArrayList();
x.add("abc");
x.add("abc");
x.add("abc");
x.add("def");
x.add("def");
x.add("ghi");
for (Object y : GetUniqueValues(x))
Log.d("something", y); //ok lets print the value
To yield the result of "abc, def, and ghi"
To be clear I agree with Travis to ask why you have duplicates. The above is to answer the question.
I figured it out. I switched it to
private void getQuestion() {
res = getResources();
qString = res.getStringArray(R.array.questions);
arrayLength = qString.length;
qTotal = arrayLength;
}
private void getRandom() {
rnd = rgenerator.nextInt(arrayLength);
rQuestion = qString[rnd];
qString[rnd] = "used";
seperate();
}
private void seperate() {
if (rQuestion != "used") {
tokens = new StringTokenizer(rQuestion, ":");
wordCount = tokens.countTokens();
sep = new String[wordCount];
wArray = 0;
while (tokens.hasMoreTokens()) {
sep[wArray] = tokens.nextToken();
wArray++;
}
qNumber++;
} else {
if (qNumber < qTotal) {
getRandom();
} else {
startActivity(new Intent("com.example.END"));
}
}
}
It gets the array from resources, then pulls a random question from the array. It then sets that one to "used" and splits it. It also checks to see if the pulled question is "used, and if it is, it pulls another question. It also goes to the end game activity if all questions are "used"