Converting Array and copying it into a List - java

To be clear I don't have any problems and don't really need help but I wanted to ask anyway:
Let's say we have a String array
String[] sarr = new String[]{"POTATO", "TOMATO"};
and we have an enum
public enum Food{POTATO, TOMATO, PIZZA}
If I wanted to check if all Strings in sarr are present in Food, I'd do the following:
ArrayList<String> foodstrings = new ArrayList<>();
Arrays.asList(Food.values()).forEach((in) -> foodstrings.add(in.toString()));
if (!foodstrings.containsAll(Arrays.asList(sarr))) doStuff();
Is there a way to do this in less lines of code? Or simply a more elegant way?

You want to determine if all element in your array are contained in the list of food names.
A possible solution is to convert the food names to a Set (to have a O(1) contains); then, we need to determine if all elements in the array are contained in this set:
public static void main(String[] args) {
String[] sarr = new String[]{"POTATO", "TOMATO"};
Set<String> set = Arrays.stream(Food.values()).map(Enum::name).collect(Collectors.toSet());
boolean result = Arrays.stream(sarr).allMatch(set::contains);
}
In your current solution, you are mutating an external variable with forEach, which is a bad practice.

I believe a better version of the first two lines would be:
Set<String> foodstrings = Arrays.stream(Food.values()).map(Enum::name).collect(Collectors.toSet());
Using Set instead of List will improve performance of containsAll, and the code is entirely streamed, instead of using forEach and an external collector.
The if is still good, although you could just combine it all into a single statement (formatted for readability):
if (! Arrays.stream(Food.values())
.map(Enum::name)
.collect(Collectors.toSet())
.containsAll(Arrays.asList(sarr))) {
doStuff();
}

if (Stream.of(sarr).allMatch(s -> Stream.of(Food.values()).anyMatch(t -> s.equals(t.name()))))
{
// all match
}
Create a stream out of sarr (which could be any Collection of objects amenable to the Stream API introduced in Java 1.8)
We ask for the value allMatch, which only returns true if a Predicate (i.e. a function that returns true/false).
For the Predicate expected by allMatch, we provide a lambda that iterates over a second set of objects via a stream, and calls anyMatch: a simple Predicate that will return true if any member object satisfies a provided condition (once again, a boolean function).
We provide yet another lambda to anyMatch which compares members of the 2 collections via their equals implementations.
This solution is semantically equivalent to the invariant
A \subset B
which in our case is
sarr \subset Food.values()
and the following Java < 1.8 code shown below with short-circuiting to mimic the specification (minus the streams overhead):
// assume success, since if both sets are empty the invariant holds
boolean subset = true;
for (String a : sarr)
{
if (null == a) continue;
boolean contained = false;
for (Food b : Food.values())
if (b.name().equals(a)) { contained = true; break; }
if (!contained) { subset = false; break; }
}
if (subset)
{
// all match
}
You could, of course, substitute different collection types and conditions, as well as use parallelStream() to make better use of the hardware available.

Related

Is there a way to get the index of an object in a list without using a loop

The trick is that this object MediaContainerModel inherits equals(Object) directly from Object and I can't and don't want to override equals in its class definition. This is what I have at the moment:
private void addMediaContainer(MediaContainerModel newMediaContainer, ProductModel product) {
List<MediaContainerModel> galleryImages = new ArrayList<>(product.getGalleryImages());
MediaContainerModel inserted = null;
// if a MediaContainer with same qualifier as newMediaContainer already exists, replace it
// with the new one to prevent duplicates
for (int i = 0; i < galleryImages.size(); i++) {
if (galleryImages.get(i).getQualifier().equals(newMediaContainer.getQualifier())) {
inserted = galleryImages.set(i, newMediaContainer);
}
}
// if not, add it
if (inserted == null) {
galleryImages.add(newMediaContainer);
galleryImages.sort((image1, image2) -> image2.getQualifier().compareTo(image1.getQualifier()));
}
product.setGalleryImages(galleryImages);
}
I want to do the same thing without the ugly for-loop by overriding MediaContainerModel.equals(Object) for this method only so I can use List.indexOf(Object) or something with lambdas. Is this possible in Java? If so how? Thanks!
without using a loop
I bet you are looking for the java-stream way:
List<MediaContainerModel> galleryImages = new ArrayList<>(product.getGalleryImages());
galleryImages.stream()
.filter(image -> newMediaContainer.getQualifier() // filter the equal ones
.equals(image.getQualifier()))
.findAny() // find any existing
.ifPresent(image -> { // add if present
galleryImages.add(newMediaContainer);
galleryImages.sort(Comparator.comparing(MediaContainerModel::getQualifier));
});
product.setGalleryImages(galleryImages);
Few notes:
The filtering uses exhaustive iteration as well as for-loop which means that all elements are iterated and multiple equal MediaContainerModel objects with same qualifiers. That's fine as long as you want to find if there is any qualified (findAny). Otherwise, to find the last one, you have to replace the line with:
.reduce((first, second) -> second)
The result using Java Stream API is a bit clumsy. I see you insert a new element and sort the list which means your intention is to keep the list always sorted. If there are no duplicate values allowed, I recommend using rather TreeSet which keeps the elements sorted upon addition or deletion. The whole solution would be the way easier:
Set<MediaContainerModel> galleryImages = new TreeSet<>(Comparator.comparing(MediaContainerModel::getQualifier));
galleryImages.addAll(product.getGalleryImages());
galleryImages.add(newMediaContainer); // won't be added if is already present
product.setGalleryImages(new ArrayList<>(galleryImages));
... if the ProductModel uses Collection or Set over List, then the last line is more straightforward:
product.setGalleryImages(galleryImages);

Best way to check if list contains at least one of an enum

I have an enum :
public enum PermissionsEnum {
ABC("Abc"),
XYZ("Xyz"),
....
}
And then I have a list of Strings. I want to check if my list has at least one of the enums. I currently check it by an iterative approach. I also know there is a way to do it by using || checking list.contains(enum.ABC..) || list.contains(enum.XYZ) || ....
Is there a better way to do it?
A List<String> never contains a PermissionsEnum value.
The condition list.contains(enum.ABC) || list.contains(enum.XYZ) is not going to be working.
Instead, you could map PermissionsEnum.values() to a Stream<String> and call Stream#anyMatch on it:
boolean result = Arrays.stream(PermissionsEnum.values())
.map(PermissionsEnum::getValue)
.anyMatch(list::contains);
*I assumed that constructor parameter is accessible by the getValue method.
In case the list is large (a few iterations over it might take a lot of time) we could optimise the previous snippet a bit and iterate over the list once:
Set<String> values = Arrays.stream(PermissionsEnum.values())
.map(PermissionsEnum::getValue)
.collect(Collectors.toSet());
boolean result = list.stream().anyMatch(values::contains);
You can do it easily in an iterative way with a for loop.
boolean contains = false;
for (PermissionsEnum permission : PermissionsEnum.values()) {
if (list.contains(permission.getName())) {
contains = true;
break;
}
}
Or you can use Collections.disjoint() like this:
Set<String> permissionsNames = Stream.of(PermissionsEnum.values())
.map(PermissionsEnum::getName())
.collect(Collectors.toSet());
boolean contains = !Collections.disjoint(list, permissionsNames);
PS: getName() must retrieve the constructor value.
Another approach is to use java.util.List.containsAll(Collection<?> c) which returns true if this list contains all of the elements of the specified collection.
List permissionEnumList = Arrays.asList(PermissionsEnum.values());
List tobeCheckedEnumList= Arrays.asList(PermissionsEnum.ABC);
boolean result= permissionEnumList.containsAll(tobeCheckedEnumList);

Erasing duplicates in ArrayList<SearchResults>

I have a POJO class SearchResults, that contains 4 Strings (title, number, date, status) and then all the getter and setter methods for it.
In another class I populate an ArrayList<SearchResults> results, is there a way I can go through that list results and erase any elements that have a duplicate number?
I've tried populating a new ArrayList by first passing results into a LinkedHashSet but that didn't work.
ArrayList<SearchResults> noDup;
noDup = new ArrayList<SearchResults>(new LinkedHashSet<SearchResults>(results));
I've also tried doing a .remove(indexof()) but that didn't work either.
if(noDup.contains(new SearchResults("-1","","",""))){noDup.remove(noDup.indexOf(new SearchResults("-1","","","")));}
Any suggestions?
Edit:
The equals() method in SearchResults (wonr refers to the number)
#Override
public boolean equals(Object object){
if(object == null){
return false;
}
if(getClass() != object.getClass()){
return false;
}
SearchResults result = (SearchResults) object;
if((this.wonr == null) ? (result.wonr == null): this.wonr.equals(result.wonr)){
return false;
}
return true;
}
The suggestions for implementing hashCode and equals are possible options, but does this single number value truly define what it means for these objects to be equivalent in the general case? If not, defining equals and hashCode that way seems to be a hack.
Without altering the definition of equivalence, if in just this case you want to elminiate values with the same number value, there are other approaches you can try. You didn't give us the API for your SearchResult class, so I'll assume there's an accessible field named number.
One quick way is to use a TreeSet which defines its idea of equivalence based on an underlying comparison operation. Write a custom Comparator that only looks at the number field and you're good to go:
Java 8
List<SearchResult> allResultsWithDuplicates = // ... populated list
Comparator<SearchResult> comparator =
(left, right) -> Integer.compare(left.number, right.number);
Set<SearchResult> uniqueNumbers = new TreeSet<>(comparator);
uniqueNumbers.addAll(allResultsWithDuplicates);
As JB Nizet mentioned, if your SearchResult class has a getNumber accessor method you can use a function reference and eliminate the lambda expression defining Comparator:
Comparator<SearchReult> comparator = Comparator.comparing(SearchResult::getNumber);
Java 5-7
In earlier versions of Java you must implement the Comparator class yourself. Then it plugs into the code given above in exactly the same way. This example assumes there is a int getNumber() accessor method on your SearchResult class:
Comparator<SearchResult> comparator =
new Comparator<SearchResult>() {
#Override
public int compare(SearchResult sr1, SearchResult sr2) {
// Optional support for null arguments is left as
// an exercise for the reader.
return Integer.compare(sr1.getNumber(), sr2.getNumber());
}
};
Another way you can do it with Java-8 is this way:
1) Create set of unique numbers,
2) Iterate over your list and filter by this set:
Set<Integer> numbers = new HashSet<>();
List<SearchResult> noDups = listWithDups.stream()
.filter(sr -> numbers.add(sr.getNumber()))
.collect(Collectors.toList());
If you implemented equals() and hashCode() so that they just look at Number property you could build a Set<SearchResult> instead of a ArrayList<SearchResult> and you will implicitly get no duplicates (this is one of the properties of sets - they don't contain duplicates). You can still iterate over the entries in the set so you should have all the functionality you need.
Do a stream of our list and use filter method and collect to an other list.

Iterating through a TreeSet in java and updating it

I have a TreeSet in Java and I have my own comparator function for this tree set. Now I am traversing this tree set using descendingIterator() method and changing the elements. So does this update the actual tree set as well wrt to the way it is sorted with my custom comparator? Or do I need to remove the element and put back the updated element?
You need to remove the element and add it back. The position of the element in the tree is decided when the element is inserted, by comparing it with other elements. If you change the object so that the comparison to other elements changes, you must remove the element first, then change it, then re-add it.
Note that removing the element while iterating will only work using the iterator's remove method. And you won't be able to add it during the iteration without getting a ConcurrentModificationException, AFAIK. So store it in a list of elements to be re-added to the set once the iteration has ended.
If you modify any part of the object that is a part of the "key" (as defined by your custom comparator) you need to remove and re-insert the object for the tree set to "learn" about the change. You should not be doing it while you are iterating, either: a good approach is to collect items that need changing in one loop, and then modify and re-insert them in another loop.
As a general rule of thumb, it isn't advisable to "modify" any value types added to Java containers which rely on equality, hash code etc. given that none of the known standard containers perform auto-balancing or adjustment in response to the change of values (which makes sense).
Along with Set, this rule is equally valid for Map types. If you are iterating over a map and modify the "key" in-place, things go bad. This is the reason why it is recommended to have immutable types as your Map keys (think of String, Integer etc.) Your case can be demonstrated by a simple example:
public class Test {
public static void main(final String[] args) {
Mutable m1 = new Mutable(1);
Mutable m2 = new Mutable(2);
Mutable m3 = new Mutable(3);
Mutable m4 = new Mutable(4);
TreeSet<Mutable> ts = new TreeSet<Mutable>(new Cmp());
ts.add(m1); ts.add(m2); ts.add(m3); ts.add(m4);
System.out.println(ts);
for (Iterator<Mutable> iter = ts.iterator(); iter.hasNext(); ) {
Mutable m = iter.next();
if (m.i == 1 || m.i == 3) {
m.i = m.i + 10;
}
}
System.out.println(ts);
}
}
class Mutable {
public int i;
public Mutable(int i) {
this.i = i;
}
public String toString() {
return "Mutable[" + i + "]";
}
}
class Cmp implements Comparator<Mutable> {
#Override public int compare(Mutable o1, Mutable o2) {
return Integer.valueOf(o1.i).compareTo(Integer.valueOf(o2.i));
}
}
Output:
[Mutable[1], Mutable[2], Mutable[3], Mutable[4]]
[Mutable[11], Mutable[2], Mutable[13], Mutable[4]]

How to lowercase every element of a collection efficiently?

What's the most efficient way to lower case every element of a List or Set?
My idea for a List:
final List<String> strings = new ArrayList<String>();
strings.add("HELLO");
strings.add("WORLD");
for(int i=0,l=strings.size();i<l;++i)
{
strings.add(strings.remove(0).toLowerCase());
}
Is there a better, faster way? How would this example look like for a Set? As there is currently no method for applying an operation to each element of a Set (or List) can it be done without creating an additional temporary Set?
Something like this would be nice:
Set<String> strings = new HashSet<String>();
strings.apply(
function (element)
{ this.replace(element, element.toLowerCase();) }
);
Thanks,
Yet another solution, but with Java 8 and above:
List<String> result = strings.stream()
.map(String::toLowerCase)
.collect(Collectors.toList());
This seems like a fairly clean solution for lists. It should allow for the particular List implementation being used to provide an implementation that is optimal for both the traversal of the list--in linear time--and the replacing of the string--in constant time.
public static void replace(List<String> strings)
{
ListIterator<String> iterator = strings.listIterator();
while (iterator.hasNext())
{
iterator.set(iterator.next().toLowerCase());
}
}
This is the best that I can come up with for sets. As others have said, the operation cannot be performed in-place in the set for a number of reasons. The lower-case string may need to be placed in a different location in the set than the string it is replacing. Moreover, the lower-case string may not be added to the set at all if it is identical to another lower-case string that has already been added (e.g., "HELLO" and "Hello" will both yield "hello", which will only be added to the set once).
public static void replace(Set<String> strings)
{
String[] stringsArray = strings.toArray(new String[0]);
for (int i=0; i<stringsArray.length; ++i)
{
stringsArray[i] = stringsArray[i].toLowerCase();
}
strings.clear();
strings.addAll(Arrays.asList(stringsArray));
}
You can do this with Google Collections:
Collection<String> lowerCaseStrings = Collections2.transform(strings,
new Function<String, String>() {
public String apply(String str) {
return str.toLowerCase();
}
}
);
If you are fine with changing the input list here is one more way to achieve it.
strings.replaceAll(String::toLowerCase)
Well, there is no real elegant solution due to two facts:
Strings in Java are immutable
Java gives you no real nice map(f, list) function as you have in functional languages.
Asymptotically speaking, you can't get a better run time than your current method. You will have to create a new string using toLowerCase() and you will need to iterate by yourself over the list and generate each new lower-case string, replacing it with the existing one.
Try CollectionUtils#transform in Commons Collections for an in-place solution, or Collections2#transform in Guava if you need a live view.
This is probably faster:
for(int i=0,l=strings.size();i<l;++i)
{
strings.set(i, strings.get(i).toLowerCase());
}
I don't believe it is possible to do the manipulation in place (without creating another Collection) if you change strings to be a Set. This is because you can only iterate over the Set using an iterator or a for each loop, and cannot insert new objects whilst doing so (it throws an exception)
Referring to the ListIterator method in the accepted (Matthew T. Staebler's) solution. How is using the ListIterator better than the method here?
public static Set<String> replace(List<String> strings) {
Set<String> set = new HashSet<>();
for (String s: strings)
set.add(s.toLowerCase());
return set;
}
I was looking for similar stuff, but was stuck because my ArrayList object was not declared as GENERIC and it was available as raw List type object from somewhere. I was just getting an ArrayList object "_products". So, what I did is mentioned below and it worked for me perfectly ::
List<String> dbProducts = _products;
for(int i = 0; i<dbProducts.size(); i++) {
dbProducts.add(dbProducts.get(i).toLowerCase());
}
That is, I first took my available _products and made a GENERIC list object (As I were getting only strings in same) then I applied the toLowerCase() method on list elements which was not working previously because of non-generic ArrayList object.
And the method toLowerCase() we are using here is of String class.
String java.lang.String.toLowerCase()
not of ArrayList or Object class.
Please correct if m wrong. Newbie in JAVA seeks guidance. :)
Using JAVA 8 parallel stream it becomes faster
List<String> output= new ArrayList<>();
List<String> input= new ArrayList<>();
input.add("A");
input.add("B");
input.add("C");
input.add("D");
input.stream().parallel().map((item) -> item.toLowerCase())
.collect(Collectors.toCollection(() -> output));

Categories

Resources