Check if custom List contains an Item - java

I've got an custom List and want to check if it contains a special Item. TheList is populated with Rowlayout objects.
public RowLayout(String content, int number) {
this.content = content;
this.number = number;
}
Now i wanna check if my List<Roalayout> contains a special item at the content - position. How do I do that?
It doesn't work with just asking .contains'.
What i wanna check:
if (!List<RowLayout>.contains("insert here"){
//Do something
}

If you can edit the class RowLayout just override hashCode and equals with whatever equality you want for them.
If you can't and have java-8 for example, this could be done:
String content = ...
int number = ...
boolean isContained = yourList.stream()
.filter(x -> x.getContent().equals(content))
.filter(x -> x.getNumber() == number)
.findAny()
.isPresent();
You can obviously return the instance you are interested in from that Optional from findAny.

You just need to override equals for List.contains to work accordingly. List.contains says in the documentation:
Returns true if and only if this list contains at least one element e
such that(o==null ? e==null : o.equals(e)).
Your implementation of equals may look like this:
class RowLayout {
private String content;
private int number;
public boolean equals(Object o)
{
if (!(o instanceof RowLayout)) return false;
final RowLayout that = (RowLayout) o;
return this.content.equals(that.content) && this.number == that.number;
}
}
Don't forget to also override hashCode, else your class will not work in hash-based structures like HashSets or HashMaps.
Example usage:
myList.contains(new RowLayout("Hello", 99));
An alternative Java 8 solution if you only care about the content and don't care about the number would be to do this:
boolean isContained = myList.stream()
.map(RowLayout::getContent)
.anyMatch("some content");

Related

How to sort by two fields one of which is enum?

I need to sort a List in alphabetical order by name and then to sort it one more time by type and put on the top of the list elements with specific type. This is what I have done so far, but it didn't work as expected and it returns the list sorted only by name.
public List<PRDto> present(
List<ParticipantReference> participants) {
return participants.stream()
.map(MAPPER::toDto)
.sorted(Comparator.comparing(PRDto::getName)
.thenComparing(PRDto::getParticipantType, (type1, type2) -> {
if (type1.equals(type2)) {
return 0;
}
if (type2.equals(ParticipantType.S.getDescription())) {
return 1;
}
if (type1.equals(ParticipantType.S.getDescription())) {
return -1;
}
return type1.compareTo(type2);
}))
.collect(toList());
}
This is my enum:
#Getter
public enum ParticipantType {
F("F"),
D_F("D+F"),
S("S");
private final String description;
ParticipantType(String description) {
this.description = description;
}
}
To preserve readability I would not leave the comparison in the stream pipeline but extract it in a comparator
public List<PRDto> present(List<ParticipantReference> participants) {
Comparator<PRDto> byType = Comparator.comparing(o -> !o.getType().equals(ParticipantType.S));
Comparator<PRDto> byName = Comparator.comparing(PRDto::getName);
return participants.stream().sorted(byType.thenComparing(byName)).collect(toList());
}
My answer had unnecessary redundant logic, which #Holger thankfully pointed out to me. This could also be inlined as Holger mentions it in the comments to:
return participants.stream().sorted(
Comparator.comparing((PRDto o) -> !o.getType().equals(ParticipantType.S))
.thenComparing(PRDto::getName))
.collect(toList());
Note that if you want to sort by type first and then by name you need to do so. Doing it the other way round would require the sort algorithm to keep the existing order for equal elements which might not always be guaranteed.
It would also be easier to map the enum to an integer representing the order and let Comparator do the work.
So your code could look like this:
...
.sorted(Comparator.comparingInt(p -> { //first compare by mapped type
switch( p.getParticipantType() ) {
case S: return 0;
default: return Integer.MAX_VALUE; //so you could insert other enum values if needed
}
}).thenComparing(PRDto::getName) //then compare by name
)
...
If you use a composite comparator like this it will put elements with type S at the top. If you'd write that comparator the "old" way it could look like this:
compare(PRDto left, PRDto right) {
int typeOrderLeft = mapToInt(left.getParticipantType()); //mapToInt contains the mapping expression, i.e the switch in the example above
int typeOrderRight = mapToInt(right.getParticipantType());
//first order by type
int result = Integer.compare(typeOrderLeft, typeOrderRight);
//if the type order is the same, i.e. both have S or non-S, sort by name
if( result == 0 ) {
result = String.compare(left.getName(), right.getName());
}
return result;
}
Example (already ordered):
type name
---------------
S zulu //S < F and S < D_F due to the mapping, name is irrelevant here
D_F alpha //F and D_F are considered equal, so names are compared
F bravo

How to check if an ArrayList of Strings contains substrings of another ArrayList of Strings?

List<String> actualList = Arrays.asList ("mother has chocolate", "father has dog");
List<String> expectedList = Arrays.asList ("mother", "father", "son", "daughter");
Is there a way to check if expectedList contains any substrings of the strings in actualList?
I found a nested for-each solution:
public static boolean hasAny(List<String> actualList, List<String> expectedList) {
for (String expected: expectedList)
for (String actual: actualList)
if (actual.contains(expected))
return true;
return false;
}
I was trying to a find lambda solution, but I could not. All the methods I found check for String#equals and not for String#contains.
It would be nice to have something like:
CollectionsUtils.containsAny(actualList, exptectedList);
But it compares strings using String#equals not String#contains.
EDIT:
Based on questions: I want to get TRUE if ALL subStrings from actualList are part of expectedList.
And solution from Kevin below works for me.
How about something like this:
list1.stream().allMatch(s1 -> list2.stream().anyMatch(s2 -> s1.contains(s2)))
Try it online.
allMatch will check if everything is true
anyMatch will check if at least one is true
Here something similar in Java 7 style without lambdas and streams to understand a bit better what is going on:
boolean allMatch = true; // Start allMatch at true
for(String s1 : list1){
boolean anyMatch = false; // Start anyMatch at false inside the loop
for(String s2 : list2){
anyMatch = s1.contains(s2);// If any contains is true, anyMatch becomes true as well
if(anyMatch) // And stop the inner loop as soon as we've found a match
break;
}
allMatch = anyMatch; // If any anyMatch is false, allMatch becomes false as well
if(!allMatch) // And stop the outer loop as soon as we've found a mismatch
break;
}
return allMatch;
Try it online.
If you prefer to have a CollectionsUtils.containsAny(list1, list2) you can reuse elsewhere in your code, you could always make one yourself:
public final class CollectionsUtil{
public static boolean containsAny(ArrayList<String> list1, ArrayList<String> list2){
return list1.stream().allMatch(s1 -> list2.stream().anyMatch(s2 -> s1.contains(s2)));
// Or the contents of the Java 7 check-method above if you prefer it
}
private CollectionsUtil(){
// Util class, so it's not initializable
}
}
Which can then be used as you wanted:
boolean result = CollectionsUtils.containsAny(actualList, expectedList);
Try it online.
I am 99% sure you are not looking for hasAny like the most upvoted answer here, but instead you want to see if all from expectedList are contained in any String in actualList. For that it would be beneficial to first create a Set and work of that (since contains is O(1) for HashSet and opposed to O(n) for List).
Think about it now, since all you want is contains, you can split that actualList and create unique words from that:
private static boolean test(List<String> actualList, List<String> expectedList) {
Pattern p = Pattern.compile("\\s+");
Set<String> set = actualList.stream()
.flatMap(p::splitAsStream)
.collect(Collectors.toSet());
return expectedList.stream().allMatch(set::contains);
}
Kevin answer is better one, but another approach is to overriding the equals method of Wrapper object.
import org.springframework.util.CollectionUtils;
class Holder {
public String obj;
public Holder(String obj) {
this.obj = obj;
}
#Override
public boolean equals(Object holder) {
if (!(holder instanceof Holder))
return false;
Holder newH = ((Holder) holder);
if (newH == null || newH.obj == null || obj == null)
return false;
return obj.contains(newH.obj) || newH.obj.contains(obj); //actually it's should be one directed.
}
}
CollectionUtils.containsAny(
actual.stream().map(Holder::new).collect(Collectors.toList()),
expected.stream().map(Holder::new).collect(Collectors.toList())
);
public static boolean containsAny(List<String> actualList, List<String> expectedList) {
final Pattern words = Pattern.compile("\\s+");
return actualList.stream()
.flatMap(words::splitAsStream)
.distinct()
// .allMatch(expectedList::contains)
.anyMatch(expectedList::contains);
}

Filter List of labels

I have a list of labels which I want to filter and leave only labels "low" "lowest" "high"
I tried to implement this:
private List<Label> filterPriorityLabels(List<Label> labels)
{
for (ListIterator<Label> iter = labels.listIterator(); iter.hasNext();)
{
Label a = iter.next();
if (a.getName() != "low" | "lowest" | "high")
{
iter.remove();
}
}
return labels;
}
But I can't get the working example. How I can fix this code?
Don't compare String with != but !equals().
And a finer solution would be to use contains() method of List.
List<String> acceptableNames = Arrays.asList("low","lowest","high");
if (!acceptableNames.contains(a.getName()))
Here's a complete example based on the answer by #davidxxx:
private static final List<String> acceptableNames =
Arrays.asList("low", "lowest", "high");
private List<Label> filterPriorityLabels(List<Label> labels)
{
for (ListIterator<Label> iter = labels.listIterator(); iter.hasNext();)
{
final Label a = iter.next();
if (!acceptableNames.contains(a.getName())
{
iter.remove();
}
}
return labels;
}
If you're using Java 8, there's a nicer way using streams:
private static final List<String> acceptableNames =
Arrays.asList("low", "lowest", "high");
private List<Label> filterPriorityLabels(List<Label> labels)
{
return labels.stream()
.filter( p -> acceptableNames.contains(p.getName()) )
.collect(Collectors.toList());
}
Note, though, that unlike davidxxx's answer, this does not modify the original list and return it. Instead, it leaves the original list unchanged and returns a new list.
if (! (a.getName().equals("low") || a.getName().equals("lowest") || a.getName().equals("high")))
in Java, you compare Strings (and objects in general), with equals(), not ==
the or logical operator is ||, not |
it expects several boolean expressions as operands. Not Strings.
Also, the signature of your method leads to think that the method creates another list, containing the filtered elements of the original list, whereas it actually modifies the list passed as argument. It should return void, or create and return a copy.

Comparing two Lists of a class without iterating the lists

I have a class Abc as below
public class Abc {
int[] attributes;
Abc(int[] attributes){
this.attributes = attributes;
}
}
Overriding the Abc hash code as below
#Override
public int hashCode() {
int hashCode = 0;
int multiplier = 1;
for(int i = attributes.length-1 ; i >= 0 ; i++){
hashCode = hashCode+(attributes[i]*multiplier);
multiplier = multiplier*10;
}
return hashCode;
}
I am using above class to create a list of objects and I want to compare whether the two lists are equal i.e. lists having objects with same attributes.
List<Abc> list1 ;
list1.add(new Abc(new int[]{1,2,4}));
list1.add(new Abc(new int[]{5,8,9}));
list1.add(new Abc(new int[]{3,4,2}));
List<Abc> list2;
list2.add(new Abc(new int[]{5,8,9}));
list2.add(new Abc(new int[]{3,4,2}));
list2.add(new Abc(new int[]{1,2,4}));
How can I compare the above two lists with/without iterating over each list . Also is there any better way to override the hashcode , so that two classes having the same attributes(values and order) should be equal.
You have to override the function equals in your class Abc. If you are using an IDE, it can be used to generates something good enough. For example, Eclipse produces the following:
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Abc other = (Abc) obj;
if (!Arrays.equals(attributes, other.attributes)) {
return false;
}
return true;
}
With this equals method, you can now check that two instance of Abc are equal.
If you want to compare your two lists list1 and list2, unfortunately you can not simply do
boolean listsAreEqual = list1.equals(list2); // will be false
because that would not only check if the elements in the lists are the same but also if they are in the same order. What you can do is to compare two sets, because in sets, the elements have no order.
boolean setAreEqual = new HashSet<Abc>(list1).equals(new HashSet<Abc>(list2)); // will be true.
Note that in that case, you should keep your implementation of hashcode() in Abc, for the HashSet to function well. As a general rule, a class that implements equals should also implement hashcode.
The problem with a Set (HashSet are Set) is that by design it will not contain several objects which are equal with each other. Objects are guaranteed to be unique in a set. For example, if you add a new new Abc(new int[]{5,8,9}) in the second set, the two sets will still be equal.
If it bothers you then the possible solution is either to compare two lists, but after having sorted them beforehand (for that you have to provide a comparator or implements compareTo), or use Guava's HashMultiset, which is an unordered container that can contain the same objects multiple times.
Override the equals method to compare objects. As the comments mention, you should be overriding the hashcode method as well when overriding equals method.
By this
so that two classes having the same attributes(values and order) should be equal.
i think you mean two objects having same attributes.
you can try something like this
public boolean equals(Object o) {
if(!(Object instanceOf Abc)) {
return false;
}
Abc instance = (Abc)o;
int[] array = instance.attributes;
for(i=0;i<array.length;i++){
if(array[i]!=this.attributes[i]) {
return false;
}
}
}
Edit: As for the hashcode the concept is that when
object1.equals(object2)
is true, then
object1.hashcode()
and
object2.hashcode()
must return the same value. and hashCode() of an object should be same and consistent through the entire lifetime of it. so generating hashcode based on the value of its instance variables is not a good option as a different hashcode may be generated when the instance variable value changes.

How to remove duplicates from a list based on a custom java object not a primitive type?

Before I post this question, I found somehow similar question posted here. But the answer was based on a String. However, I have a different situation here. I am not trying to remove String but another object called AwardYearSource. This class has an int attribute called year. So I want to remove duplicates based on the year. i.e if there is year 2010 mentioned more than once, I want to remove that AwardYearSource object. How can I do that?
The simplest way to remove elements based on a field is as follows (preserving order):
Map<Integer, AwardYearSource> map = new LinkedHashMap<>();
for (AwardYearSource ays : list) {
map.put(ays.getYear(), ays);
}
list.clear();
list.addAll(map.values());
Another way would be to override hashCode() and equals(Object obj) for your object. Since it just has one field you want to use to determine equality, this is pretty straightforward. Something like:
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof AwardYearSource)) {
return false;
}
return (this.year == ((AwardYearSource)obj).year);
}
public int hashCode() {
return this.year;
}
Then you can just stick all of the objects into a Set to remove duplicates:
Set<AwardYearSource> set = new Set<AwardYearSource>();
set.add(new AwardYearSource(2011));
set.add(new AwardYearSource(2012));
set.add(new AwardYearSource(2011));
for (AwardYearSource aws : set) {
System.out.println(aws.year);
}
Fairly simply. Although something bugs me about the map versions (not that I doubt they'd work, it just seems like overkill, somehow - although this version isn't necessarily any better in that regard).
Answer is functional, and threadsafe (assuming AwardYearSource is immutable).
public static List<AwardYearSource> removeDuplicateYears(
final Collection<AwardYearSource> awards) {
final ArrayList<AwardYearSource> input = new ArrayList<AwardYearSource>(awards);
// If there's only one element (or none), guaranteed unique.
if (input.size() <= 1) {
return input;
}
final HashSet<Integer> years = new HashSet<Integer>(input.size(), 1);
final Iterator<AwardYearSource> iter = input.iterator();
while(iter.hasNext()) {
final AwardYearSource award = iter.next();
final Integer year = award.getYear();
if (years.contains(year)) {
iter.remove();
} else {
years.add(year);
}
}
return input;
}
You could use a map and store your objects with the year as a key:
Map<Integer, AwardYearSource> map = new HashMap<Integer, AwardYearSource>();
map.put(someAwardYearSource1.getYear(), someAwardYearSource1);
map.put(someAwardYearSource2.getYear(), someAwardYearSource2);
etc.
At the end the map will contain unique values by year, which you can call with the values method:
Collection<AwardYearSource> noDups = map.values();
Create a HashMap object with int as the key type and your class as the value type. Then iterate over the list and insert each element to the map using:
mymap.put(source.year, source);
Then remove all elements from the origianl list and iterate over the map and insert each element to the list.
If your AwardYearSource class overrides equals and hashcode methods (Eclipse can generate both), then you can add them to a Set. The Set will not contain any duplicates.
public class AwardYearSource
{
private final int year;
public AwardYearSource(int year)
{
this.year = year;
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + year;
return result;
}
#Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AwardYearSource other = (AwardYearSource) obj;
if (year != other.year)
return false;
return true;
}
#Override
public String toString()
{
return String.valueOf(year);
}
public static void main(String[] args)
{
Set<AwardYearSource> set = new HashSet<AwardYearSource>();
set.add(new AwardYearSource(2000));
set.add(new AwardYearSource(2000));
set.add(new AwardYearSource(2000));
set.add(new AwardYearSource(2000));
System.out.println(set);
}
}
The output is [2000]. Only one item in the set.
Set<Integer> set = new HashSet<>();
list.removeIf(i -> set.contains(i.getYear()) ? true : !set.add(i.getYear()));
This should help wherein, duplication is decided based on certain property (or combination of properties), year in this case. Hope this helps.

Categories