I have two lists of Strings and am removing duplicates like this:
List<String> list1 = Arrays.asList("1", "2", "3", "4");
List<String> list2 = Arrays.asList("1", "4", "5", "6");
List<String> duplicates = list1.stream().filter(s -> list2.contains(s)).collect(Collectors.toList());
list1.removeAll(duplicates);
list2.removeAll(duplicates);
So the result is:
list1 = 2, 3
list2 = 5, 6
Is there a better way to accomplish this? i.e. with fewer statements.
You can use removeAll which is defined in Collection interface.
boolean removeAll(Collection<?> c)
Removes all of this collection's elements that are also contained in
the specified collection (optional operation). After this call
returns, this collection will contain no elements in common with the
specified collection.
// init
List<String> sourceList1 = Arrays.asList("1", "2", "3", "4");
List<String> sourceList2 = Arrays.asList("1", "4", "5", "6");
// you need to create duplicate collection, because removeAll modify collection
List<String> resultList1 = new ArrayList(sourceList1);
List<String> resultList2 = new ArrayList(sourceList2);
//remove duplicates from collections
resultList1.removeAll(sourceList2); // second from first
resultList2.removeAll(sourceList1); // first from second
One of the possibilities worth considering is to create Set<String> and add these lists to it. Set allows adding only unique values to itself, it prevents adding duplicates.
The first way to use Set: Create a Set containing an intersection of both lists. Adding to new, getting rid of duplicates lists is taking place only if you checked that every object of the source is not present in previously created Set of duplicates.
Second way (only if your lists doesn't care about holding duplicates itself - for example in the first you have two times the same value existing): Create a Set for the first and for the second list, and add these lists to them and after that check for duplicates.
As I mentioned in comments I could misunderstood question and looked for "another", not for "more efficient" way of achieving what you're asking for, but maybe it could actually be helpful nonetheless.
Related
Consider the following pseudo Map<String, Set<String>>:
{
"1": ["A", "B"],
"2": ["A", "C"],
"3": ["D", "B", "A", "C"],
"4": ["C", "A", "B"],
"5": ["A", "B"],
}
What is the best way to join the value Sets into a single Set (above example should be ["A", "B", "C", "D"]). The order of the resulting set does not matter.
I know that I can to something like this:
Collection<Set<String>> values = myMap.values();
Set<String> unique = new HashSet<>();
for (Set<String> v : values) {
for (String s : v) {
if (!unique.contains(s)) unique.add(s);
}
}
But it feels kind of ugly and I'm wondering if there is a better (and more "built-in") way of doing this?
Use the Set.addAll(Collection) method; see the javadoc.
Collection<Set<String>> values = myMap.values();
Set<String> unique = new HashSet<>();
for (Set<String> v : values) {
unique.addAll(v);
}
The logic should be self-evident.
Meta-lesson: it is a good idea to familiarize yourself with the capabilities of the APIs that you use by skim-reading the javadocs.
You actually don't have to check whether a Set already contains the value which you are about to add. That's why you'd want to use a set in the first place.
If this set already contains the element, the call leaves the set unchanged and returns false.
from Set#add(E) Javadoc
Keeping that in mind, the only thing you will have to do is actually go through your Sets and combine the values into a single set without having to worry about duplicates.
One solution via addAll() was already provided, so I thought I would provide an alternative solution using Java 8 streams:
Set<String> unique = myMap.values() // gets the values (all sets) from the map
.stream() // stream of values
.flatMap(Set::stream) // flattens all sets (in values) in to single stream
.collect(Collectors.toSet()); // collects the values into single set
Class java.util.Collections has method nCopies().
The documentation for this method says that it returns an immutable copy of collection containing n copies of same element.
However in the below code I am able to modify the list and add a new element to the collection. How is this possible in an immutable collection?
(See line number 3, below.)
1) List<String> list = new ArrayList<String>(Collections.nCopies(5, "Some string"));
2) System.out.println(list);
3) list.add("Some other string");
4) System.out.println(list);
Because you create a new modifiable ArrayList out of the returned list?
List<String> list = new ArrayList<String>(Collections.nCopies(5, "Chinese Virus"));
this is essentially the same as
List<String> unmodifiableList = Collections.nCopies(5, "Chinese Virus");
List<String> modifiableList = new ArrayList<>(unmodifiableList);
unmodifiableList.remove(0); // will throw UnsupportedOperationException
modifiableList.remove(0); // will work without issues
In the above example modifiableList is a List containing the same elements as unmodifiableList but otherwise it's just an ordinary ArrayList that allows adding and removing elements.
You aren't modifying the collection returned from nCopies - you've created a new ArrayList and copied the contents into it. Once the copying is done, it's just a normal ArrayList, and there's no reason you won't be able to add to it.
Looked several answers on stack, tried to do it with help of this one Simple way to compare 2 ArrayLists but can't try to figure out what seems to be a problem. To summarize the code that isnt visible, I've created two arraylists that contain 4 files names. Now im trying to get the third arraylist which will contain only unique values from these two arraylists.
Example: 1st arraylist - One, Two, Three, Four
2nd arraylist - One, Three, Five, Seven
3rd arraylist - Two, Four, Five, Seven (solution arraylist)
Here is the code:
Collection<String> filesFromDir = new
ArrayList(Arrays.asList(listOfFilenamesWithNoExtension));
Collection<String> filesFromDB = new ArrayList(Arrays.asList(listOfFilesDB));
List<String> listDir = new ArrayList<String>(filesFromDir);
List<String> listDB = new ArrayList<String>(filesFromDB);
listDir.removeAll(listDB);
listDB.removeAll(listDir);
System.out.println("Unique values: ");
System.out.println(listDir);
System.out.println(listDB);
Make a duplicate of the first list and use it to removeAll from second list. Because if you remove duplicates from first list and then compare it with second list all the values will be unique as the duplicates were already removed from first list.
Collection<String> listDir = new ArrayList(Arrays.asList("1","2", "3", "4", "5", "6", "7"));
Collection<String> listDirCopy = new ArrayList<>();
listDirCopy.addAll(listDir);
Collection<String> listDB = new ArrayList(Arrays.asList("1","3", "5", "7", "9"));
List<String> destinationList = new ArrayList<String>();
listDir.removeAll(listDB);
listDB.removeAll(listDirCopy);
destinationList.addAll(listDir);
destinationList.addAll(listDB);
System.out.println(destinationList);
You shouldn't use removeAll in this case:
listDir.removeAll(listDB);
listDB.removeAll(listDir);
Because once you remove the common element 'One' from listDir, the listDB still contains it and won't be removed by listDB.removeAll(listDir) because listDir doesn't contains it.
So you end up with listDB with it's original elements.
One possible solution would be to travers both list and check if an element is common.
Despite the lists are the same size you can travers them in the same loop.
for(int i=0;i<listDB.size();i++){
if(!listDB.contains(listDir.get(i)){
resultList.add(listDir.get(i))
}
if(!listDir.contains(listDB.get(i)){
resultList.add(listDB.get(i))
}
}
Hello sorry for my beginner code here but can you maybe make the third arraylist, loop through the first and then add all the elements in the first array list. Then loop through the second list and add the elements in the 3rd array list if it does not exist or remove if it exists.
Look at the following code, hope it helps
public void sort(ArrayList<String> one, ArrayList<String> two){
ArrayList<String> three = new ArrayList<>();
three.addAll(one);
for (int i = 0; i < two.size(); i++) {
if (three.contains(two.get(i))){
three.remove(two.get(i));
}else {
three.add(two.get(i));
}
}
}
I want a dictionary of values. The keys are all strings. Each key corresponds with some sort of list of strings. How do I make a list of strings for each key and update that accordingly? I'll explain:
I have a loop that is reading lines of a word list. The words are then converted into a string code and set as keys in the dictionary. Here is an example of the string code/word relationship.
123, [the]
456, [dog]
328, [bug]
...
However, my program keeps looping through the word list and eventually will run into a word with the same code as "the", but maybe a different word, lets say "cat". So I want the list to look like:
123, [the, cat]
456, [dog]
...
How do I get it to make an arraylist for every key that I can then add to on the fly when needed? My end goal is to be able to print out the list of words in that list for a called code (.get())
You can make a HashMap. In your case
HashMap<Integer, ArrayList<String>> works fine.
Like it has already been said, a MultiMap seems to be what you need. Guava that was already suggested and it's a good option. There is also and implementation from commons-collections you can use.
From commons-collections documentation:
MultiValuedMap<K, String> map = new MultiValuedHashMap<K, String>();
map.put(key, "A");
map.put(key, "B");
map.put(key, "C");
Collection<String> coll = map.get(key); // returns ["A", "B", "C"]
You can always implement your own MultiMap if you don't want to use an external library. Use a HashMap<String,List<String>> to store your values and wrap it with your own put, get and whatever other methods you see fit.
It sounds like you want a Multimap from the Guava library.
You can also go the route of using a Map<Integer, List<String>>, but then you will need to manually handle the case where the list is null (probably just allocate a new list in that case).
You can use a HashMap that links each id to a list of strings:
Map<String, List<String>> dictionary = new HashMap<String,List<String>>();
Now let's say you read two Strings: id and word . To add them to your dictionary, you can first verify if your id has already been read (using the containsKey() method)- in which case you just append the word to the list corresponding to that id - or, if this is not the case, you create a new list with this word:
//If the list already exists...
if(dictionary.containsKey(id)) {
List<String> appended = dictionary.get(id);
appended.add(word); //We add a new word to our current list
dictionary.remove(id); //We update the map by first removing the old list
dictionary.put(id, appended); //and then appending the new one
} else {
//Otherwise we create a new list for that id
List<String> newList = new ArrayList<String>();
newList.add(word);
dictionary.put(id, newList);
}
Then whenever you want to retrieve your list of strings for a certain id you can simply use dictionary.get(id);
You can find more information on HashMaps on the Java documentation
I assumed you didn't want repeats in your list so I used Set instead.
Map<String,Set<String>> mapToSet = new HashMap<>();
List<String []>keyvals = Arrays.asList(new String[][]{{"123","the"},{"123","cat"}});
for(String kv[] : keyvals) {
Set<String> s = mapToSet.get(kv[0]);
if(null == s) {
s = new HashSet<String>();
}
s.add(kv[1]);
mapToSet.put(kv[0], s);
}
In Java, I have an ArrayList of Strings like:
[,Hi, ,How,are,you]
I want to remove the null and empty elements, how to change it so it is like this:
[Hi,How,are,you]
List<String> list = new ArrayList<String>(Arrays.asList("", "Hi", null, "How"));
System.out.println(list);
list.removeAll(Arrays.asList("", null));
System.out.println(list);
Output:
[, Hi, null, How]
[Hi, How]
Its a very late answer, but you can also use the Collections.singleton:
List<String> list = new ArrayList<String>(Arrays.asList("", "Hi", null, "How"));
// in one line
list.removeAll(Arrays.asList("", null))
// separately
list.removeAll(Collections.singleton(null));
list.removeAll(Collections.singleton(""));
Another way to do this now that we have Java 8 lambda expressions.
arrayList.removeIf(item -> item == null || "".equals(item));
If you are using Java 8 then try this using lambda expression and org.apache.commons.lang.StringUtils, that will also clear null and blank values from array input
public static String[] cleanArray(String[] array) {
return Arrays.stream(array).filter(x -> !StringUtils.isBlank(x)).toArray(String[]::new);
}
ref - https://stackoverflow.com/a/41935895/9696526
If you were asking how to remove the empty strings, you can do it like this (where l is an ArrayList<String>) - this removes all null references and strings of length 0:
Iterator<String> i = l.iterator();
while (i.hasNext())
{
String s = i.next();
if (s == null || s.isEmpty())
{
i.remove();
}
}
Don't confuse an ArrayList with arrays, an ArrayList is a dynamic data-structure that resizes according to it's contents. If you use the code above, you don't have to do anything to get the result as you've described it -if your ArrayList was ["","Hi","","How","are","you"], after removing as above, it's going to be exactly what you need - ["Hi","How","are","you"].
However, if you must have a 'sanitized' copy of the original list (while leaving the original as it is) and by 'store it back' you meant 'make a copy', then krmby's code in the other answer will serve you just fine.
Going to drop this lil nugget in here:
Stream.of("", "Hi", null, "How", "are", "you")
.filter(t -> !Strings.isNullOrEmpty(t))
.collect(ImmutableList.toImmutableList());
I wish with all of my heart that Java had a filterNot.
There are a few approaches that you could use:
Iterate over the list, calling Iterator.remove() for the list elements you want to remove. This is the simplest.
Repeatedly call List.remove(Object). This is simple too, but performs worst of all ... because you repeatedly scan the entire list. (However, this might be an option for a mutable list whose iterator didn't support remove ... for some reason.)
Create a new list, iterate over the old list, adding elements that you want to retain to a new list.
If you can't return the new list, as 3. above and then clear the old list and use addAll to add the elements of the new list back to it.
Which of these is fastest depends on the class of the original list, its size, and the number of elements that need to be removed. Here are some of the factors:
For an ArrayList, each individual remove operation is O(N), where N is the list size. It is expensive to remove multiple elements from a large ArrayList using the Iterator.remove() method (or the ArrayList.remove(element) method).
By contrast, the Iterator.remove method for a LinkedList is O(1).
For an ArrayList, creating and copying a list is O(N) and relatively cheap, especially if you can ensure that the destination list's capacity is large enough (but not too large).
By contrast, creating and copying to a LinkedList is also O(N), but considerably more expensive.
All of this adds up to a fairly complicated decision tree. If the lists are small (say 10 or less elements) you can probably get away with any of the approaches above. If the lists could be large, you need to weigh up all of the issues in the list of the expected list size and expected number of removals. (Otherwise you might end up with quadratic performance.)
This code compiles and runs smoothly.
It uses no iterator so more readable.
list is your collection.
result is filtered form (no null no empty).
public static void listRemove() {
List<String> list = Arrays.asList("", "Hi", "", "How", "are", "you");
List<String> result = new ArrayList<String>();
for (String str : list) {
if (str != null && !str.isEmpty()) {
result.add(str);
}
}
System.out.println(result);
}
If you get UnsupportedOperationException from using one of ther answer above and your List is created from Arrays.asList(), it is because you can't edit such List.
To fix, wrap the Arrays.asList() inside new LinkedList<String>():
List<String> list = new LinkedList<String>(Arrays.asList(split));
Source is from this answer.
Regarding the comment of Andrew Mairose - Although a fine solution, I would just like to add that this solution will not work on fixed size lists.
You could attempt doing like so:
Arrays.asList(new String[]{"a", "b", null, "c", " "})
.removeIf(item -> item == null || "".equals(item));
But you'll encounter an UnsupportedOperationException at java.util.AbstractList.remove(since asList returns a non-resizable List).
A different solution might be this:
List<String> collect =
Stream.of(new String[]{"a", "b", "c", null, ""})
.filter(item -> item != null && !"".equals(item))
.collect(Collectors.toList());
Which will produce a nice list of strings :-)
lukastymo's answer seems the best one.
But it may be worth mentioning this approach as well for it's extensibility:
List<String> list = new ArrayList<String>(Arrays.asList("", "Hi", null, "How"));
list = list.stream()
.filter(item -> item != null && !item.isEmpty())
.collect(Collectors.toList());
System.out.println(list);
What I mean by that (extensibility) is you could then add additional filters, such as:
.filter(item -> !item.startsWith("a"))
... although of course that's not specifically relevant to the question.
List<String> list = new ArrayList<String>(Arrays.asList("", "Hi", "", "How"));
Stream<String> stream = list .stream();
Predicate<String> empty = empt->(empt.equals(""));
Predicate<String> emptyRev = empty.negate();
list= stream.filter(emptyRev).collect(Collectors.toList());
OR
list = list .stream().filter(empty->(!empty.equals(""))).collect(Collectors.toList());
private List cleanInputs(String[] inputArray) {
List<String> result = new ArrayList<String>(inputArray.length);
for (String input : inputArray) {
if (input != null) {
String str = input.trim();
if (!str.isEmpty()) {
result.add(str);
}
}
}
return result;
}