I have set like:
Set<String>
I need on each element of Set make split by ; and create new Set that will contain only 2-nd element. Should I make it directly one by one or exists better way?
Thanks.
If you can relax your constraint of an output being a Set<String> to being a Collection<String> you could use Guava and defer the transformation of elements until enumeration of elements through the Collections2#transform() method. You would just have to write a custom function to perform the split on an individual element.
But if you cannot/should not relax this constraint, you are best left to doing the already proposed individual iterations (as it'd be much more legible).
Code would look something like:
Set<String> input; //Given
Collection<String> output = Collections2.transform(input, new Function<String,String>() {
#Override
public String apply (String element) {
// As JohnnyO says, add appropriate edge case checking...
return element.split(";")[1];
}
});
Set<String> suffixSet = new HashSet<String>();
for (String s : inputSet) {
suffixSet.add(s.split(";")[1])
}
I'd also add appropriate error checking and handling for the case when s does not have a ; present.
As you have not shown the code, we can only guess what you're trying to do. You need to iterate through the Set and split each String. You can use split method if you want.
It is hard to say with so little information. You could iterate over the set doing split and adding to another Set.
Or you could replace Set with a e.g. HashMap and when you create the map put as key the first part of the string and as value the second so that you can retrieve the second part when you need fast.
Or if you create the strings yourself place them in different sets directly
Or...(you don't say enough) to provide more options
Related
I noticed that adding an element to a list does change its hash-key value and therefore it cannot be accessed again since .contains(modifiedObject) won't get a collision here. I did not expect that behavior to be honest. Makes me wonder how HashSet does its hashing .. So how can I make sure to not destroy my HashSet as I modify e.g. a list of strings as shown below. Is there a method to do that safe or is that just something I have to look out as a programmer?
private HashSet<List<String>> bagOfWordsMap = new HashSet<List<String>>();
private void createBagOfWordsList(UnifiedTag[] invalidTags) {
for(List<String> sentences : getSentenceList()) {
List<String> sentenceStemWords = new ArrayList<String>();
// Not what you would want to do since sentenceStemWords is
// modified right after and bagOfWordsMap.contains(sentenceStemWords)
// won't collide again:
// bagOfWordsMap.add(sentenceStemWords);
for(String word : sentences) {
String stem = Stemmer.getStem(word);
sentenceStemWords.add(stem);
}
bagOfWordsMap.add(sentenceStemWords);
}
}
Never use a mutable object as key in a map or set
Implement a frozen type that cannot be modified anymore if you want to prevent accidential modification!
fine print: it's technically okay to have mutable attributes on an object if they don't change the key, but you won't be able to access them easily by key in a java set, as there is no HashSet.get to get the current member, only a contains. Also, it's bad style and fragile. It's much better to split such objects into key, value.
One way is to use an UnmodifiableList<String> instead of a List<String> in your HashSet.
Another option is to use a HashMap<String,List<String>> instead of your HashSet<List<String>>, provided that you can associate some unique String key with each of your Lists.
Pretty new to Java here. I am coming from Python. There are similar questions on SO talking about remove or add element while iterating a Java Set. What I would like to know is to modify the elements containing in the Set. For instance, ["apple", "orange"] to ["iapple", "iorange"]. In addition, I would like to do it in place, i.e., not creating another set and put the modified element into the new set while iterating it.
Apparently a simple for loop doesn't work, as the following:
import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;
class Test {
public static void main (String[] args) {
Set<String> strs = new HashSet<String>(Arrays.asList("apple", "orange"));
for (String str : strs) {
str = "i" + str;
}
}
}
The issue with what you've written is you don't do anything with the computed value. The for-each loop sets the value of str to each element. Within the for loop you are changing the value of str, but not doing anything else with it.
This would be easy to do in a linkedlist or any data structure which supports indexing, but with a set it can be tricky. Just removing the old element and adding the new one will likely screw up the iteration, especially because you're dealing with a hash set.
A simple way to do this is to convert to a list and back:
class Test {
public static void main (String[] args) {
Set<String> strs = new HashSet<String>(Arrays.asList("apple", "orange"));
//Convert to list
LinkedList<String> strsList = new LinkedList<String>();
strsList.addAll(strs);
//Do modification
for (int i = 0; i < strsList.size(); i++) {
String str = strsList.get(i);
strsList.set(i,"i" + str);
}
//Convert back to set
strs.clear();
strs.addAll(strsList);
}
}
This is clearly a bit more work than you would expect, but if mass-replacing is behavior you anticipate then probably don't use a set.
I'm interested to see what other answers pop up as well.
You cannot modify a String in java, they are immutable.
While it is theoretically possible to have mutable elements in a Set and mutate them in place, it is a terrible idea if the mutation effects hashcode (and equals).
So the answer to your specific question is no, you cannot mutate a String value in a Set without removing then adding entries to that Set.
The problem is that in the for loop, str is merely a reference to a String. References, when reassigned, don't change the actual object it refers to. Additionally, strings are immutable anyway, so calling any method on them will give you a new String instead of modifying the original. What you want to do is store all the new Strings somewhere, take out the old ones, and then add the new ones.
EDIT: My original code would have thrown a ConcurrentModificationException.
There are 3 problems in your approach to solve the problem.
1st - You can't modify the contents of a Set while you are iterating it. You would need to create a new Set with the new values.
"But I'm not modifying the set, I am modifying the objects within it", which leads to problem two
2nd - In your code you are modifying a reference to a string, not the string itself.
To modify the string you would need to call a method over it, like string.changeTo("this"), or modify a field, string.value = "new value".
Which leads to problem three.
3rd - Strings are immutable. when you construct a string, say new String("hello"), you can't further modify it's inner value.
The solutions:
First option is the simpler one, create a new set.
The second option is to use string builders instead of strings, which are mutable string creators/placeholders.
http://docs.oracle.com/javase/7/docs/api/java/lang/StringBuilder.html
Lets say I have a list and I am trying to look up a Class element in it, from which I only know one (unique) attribute.
public static List<Achievement> listAchievements;
String idAchievement = "5764e35";
This is obviously not working
listAchievements.indexOf(idAchievement );
Neither is this
Achievement ach(idAchievement);
listAchievements.getIndexOf(ach);
and the workaround is not pretty
for (Achievement achievement : listAchievements) {
if (achievement.resourceID().equalsIgnoreCase(idAchievement)) {
// STUFF
break;
}
}
What you have isn't a workaround, it's the solution.
You could abstractify it with lambda-like behavior, and this and that... but in the end, if you're trying to search a list for an element that has a given attribute, there's nothing you can do but to iterate over the list until you find an element with that given attribute.
If you need to find Acchievments by ID more directly, a Map<String,Achievement> might be a better choice if the IDs are unique (which you say they are).
There is no other way than to loop over the elements until you find the one you're looking for. You could use Guava's support for predicates:
Achievement a = Iterables.find(list, new Predicate<Achievement>() {
#Override
public boolean apply(Achievement input) {
return input.resourceID().equalsIgnoreCase(idAchievement)
}
});
but the end result is the same.
Or you could maintain a separate Map<String, Achievement> in addition to your list, or use a LinkedHashMap<String, Achievement> instead of your list, which would achieve O(1) search instead of O(n).
1) you have to sort your list using static void sort(List,Comparator).
2) use static int binarySearch(List,Key,Comparator).
These two method are of java.util.Collections
If you only have a list there's not much more to do. But if this is something that is done often you may want to consider creating a HashMap or similar instead (from achievementId to achievement).
To make such efficient you'll either have to use Map of idAchievement to Achievement, or make sure your collection is sorted by idAchievement attribute and then use Collections.binarySearch().
If I have an ArrayList of String forming part of a class in Java like so:
private ArrayList<String> rssFeedURLs;
If I want to use a method in the class containing the above ArrayList, using ArrayList contains to check if a String is contained in this ArrayList, I believe I should be able to do so as follows:
if (this.rssFeedURLs.contains(rssFeedURL)) {
Where rssFeedURL is a String.
Am I right or wrong?
You are right. ArrayList.contains() tests equals(), not object identity:
returns true if and only if this list
contains at least one element e such
that (o==null ? e==null : o.equals(e))
If you got a NullPointerException, verify that you initialized your list, either in a constructor or the declaration. For example:
private List<String> rssFeedURLs = new ArrayList<String>();
Yes, that should work for Strings, but if you are worried about duplicates use a Set. This collection prevents duplicates without you having to do anything. A HashSet is OK to use, but it is unordered so if you want to preserve insertion order you use a LinkedHashSet.
You are right that it should work; perhaps you forgot to instantiate something. Does your code look something like this?
String rssFeedURL = "http://stackoverflow.com";
this.rssFeedURLS = new ArrayList<String>();
this.rssFeedURLS.add(rssFeedURL);
if(this.rssFeedURLs.contains(rssFeedURL)) {
// this code will execute
}
For reference, note that the following conditional will also execute if you append this code to the above:
String copyURL = new String(rssFeedURL);
if(this.rssFeedURLs.contains(copyURL)) {
// code will still execute because contains() checks equals()
}
Even though (rssFeedURL == copyURL) is false, rssFeedURL.equals(copyURL) is true. The contains method cares about the equals method.
Perhaps you need to post the code that caused your exception. If the above is all you have, perhaps you just failed to actually initialise the array.
Using contains here should work though.
Your question is not very clear.
What's your code exactly doing? Give more code.
What's the error you're getting?
You say you get a null-pointer. You cannot get a null pointer as a value returned by contains().
However you can get a NullPointerException if your list has not been initialized. By reading your question now, I'd say that what you show here is correct, but maybe you just didn't instantiate the list.
For this to work (to add a feed URL if it isn't already in the list):
if (!this.rssFeedURLs.contains(rssFeedURL)) {
this.rssFeedURLs.add(rssFeedUrl);
}
then this declaration would do:
private ArrayList<String> rssFeedURLs = new ArrayList<String>();
or initialize your list later on, but before trying to access its methods:
rssFeedUrls = new ArrayList<String>();
Finally... Do you really need a List? Maybe a Set would be better if you don't want duplicates. Use a LinkedHashSet if preserving the ordering matters.
Right...with strings...the moment you deviate from primitives or strings things change and you need to implement hashcode/equals to get the desired effect.
EDIT: Initialize your ArrayList<String> then attempt to add an item.
You're correct. As others said according to your comments, you probably did not initialize your ArrayList.
My point is different: you claimed that you're checking for duplicates and this is why you call the contains method. Try using HashSet. It should be more efficient - unless you need to keep the order of URLs for any reason.
Thanks to you all for answering so quickly. I could always use a set but I have the ArrayList working now. The problem was that in the constructor of the class containing the ArrayList, I was not saying:
public RSS_Feed_Miner() {
...
this.rssFeedURLs = new ArrayList<String>();
...
}
D'Oh! for a Friday afternoon.
ArrayList<String> newlyAddedTypes=new ArrayList<String>();
.....
newlyAddedTypes.add("test1");
newlyAddedTypes.add("test1");
newlyAddedTypes.add("test2");
if(newlyAddedTypes.contain("test"){
//called here
}
else{
}
I'm using Groovy and Grails and am trying to take a parameter passed to a controller, parse it, and add each individual element into a list. I thought this would work, but it is adding the whole string into the list, leaving me with only one element.
list = []
list.add(params["firstNames"].split())
is returning a list with size 1, with the list element being a string containing all the names.
also, if I do list = params["firstNames"].split()) , it is showing a size of 2 (i have two elements) but it is still treating it as a String and I cannot perform any other list operations on it.
what is it that I'm doing wrong?
thanks for the help.
Try a variation of this:
String foo = 'foo,bar,baz'
def list = foo.split(',') as List
assert list instanceof java.util.List
assert list.size() == 3
The key part is the as List. If this doesn't work for you, make sure you're using the correct delimiter argument to split(). If you can provide us with an example of what the parameter value might be, we can probably provide a better answer.