1

I want to write a code to check the existence of given two values in a List.

List<Tag> tags = new ArrayList<>();

The requirement is to return true only if the List tag contains both "start" and "end" values. My code is like this, but it doesn't cater to the requirement.

public static boolean checkStartAndEndTimeTag(List<Tag> tags) {
        boolean isSuccess = false;
        int count = 0;
        for (Tag tag : tags) {
            if (tag.getKey().equals("start") || tag.getKey().equals("end")) {
                count++;
                if (count == 2)
                    break;
                isSuccess = true;
            }

        }
        return isSuccess;

Can someone help me with to resolve this issue?

0

4 Answers 4

3

This...

if (count == 2)
  break;
isSuccess = true;

doesn't make sense. This will set isSuccess even if there is only one match

The long winded approach

Okay, let's assuming for a second that you only care if there is at least one start and one end (discounting duplicates). One approach would be to use to state flags, one for start and one for end. To keep it simple, they would start of as 0 but would only ever be a maximum of 1 (because we don't want duplicates), then you might be able to do something like...

public static boolean checkStartAndEndTimeTag(List<Tag> tags) {
    boolean isSuccess = false;
    int starts = 0;
    int ends = 0;
    for (Tag tag : tags) {
        if (tag.getKey().equals("start")) {
            starts = 1;
        } else if (tag.getKey().equals("end")) {
            ends = 1;
        }
    }
    isSuccess = (starts + ends) == 2;
    return isSuccess;
}

Ok, you don't need isSuccess = (starts + ends) == 2; and could simply return the result of the comparison. You could also break out of the loop if (starts + ends) == 2 and save yourself from unnecessary computation

for (Tag tag : tags) {
    if (tag.getKey().equals("start")) {
        starts = 1;
    } else if (tag.getKey().equals("end")) {
        ends = 1;
    }

    if ((starts + ends) == 2) {
        break;
    }
}

Using streams...

One approach might be to make use the streams support and simply filter the List and count the results, for example...

import java.util.ArrayList;
import java.util.List;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        List<Tag> tags = new ArrayList<Tag>(25);
        tags.add(new Tag("begin"));
        tags.add(new Tag("front"));
        tags.add(new Tag("start"));
        tags.add(new Tag("finish"));
        tags.add(new Tag("tail"));
        tags.add(new Tag("end"));

        boolean isSuccessful = tags.stream().filter(tag -> tag.getKey().equals("start") || tag.getKey().equals("end")).count() >= 2;
        System.out.println(isSuccessful);
    }

    public class Tag {
        private String key;

        public Tag(String key) {
            this.key = key;
        }

        public String getKey() {
            return key;
        }


    }
}

Updated...

Okay, this got complicated fast. Let's assume you don't want to match two start tags, so you MUST have both one end and one start tag

So, using the above, example, we can modify the Tag class to support equals (and by extension hashcode)

public class Tag {

    private String key;

    public Tag(String key) {
        this.key = key;
    }

    public String getKey() {
        return key;
    }

    @Override
    public String toString() {
        return getKey();
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 73 * hash + Objects.hashCode(this.key);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Tag other = (Tag) obj;
        if (!Objects.equals(this.key, other.key)) {
            return false;
        }
        return true;
    }

}

Then we can simply use distinct to filter out any duplicates, for example...

boolean isSuccessful = tags
        .stream()
        .distinct()
        .filter(tag -> tag.getKey().equals("start") || tag.getKey().equals("end"))
        .count() >= 2;

Probably not the most efficient solution, but certainly one of the shortest

Sign up to request clarification or add additional context in comments.

Comments

2

In this code

if (tag.getKey().equals("start") || tag.getKey().equals("end")) {
     count++;
     if (count == 2)
            break;
     isSuccess = true;
}

you are setting isSuccess to true whenever the tag is start or end.

Better way would be

if (tag.getKey().equals("start") || tag.getKey().equals("end")) {
    count++;
    if (count == 2)
        return true;
}

4 Comments

That would erroneously return true if the array contains "start" twice.
I just realized that mistake was in the original code to begin with...
@daniu Furthermore it erroneously return true if the array contains "end" twice - yeah I noticed this when writing the question but forgot to mention.
Okay! It works fine. I set isSuccess = true inside if loop and return isSuccess out of the for loop. Thankyou!
2

You could also use

tags.stream()
    .map(Tag::getKey)
    .distinct()
    .filter(t -> "start".equals(t) || "end".equals(t))
    .count() == 2;

This would also fix the issue that your original code falsely returns true if the list contains statt or end twice.

6 Comments

Interesting. I tried your code against the test data from my answer ([begin, front, start, finish, tail, start]) and it returns true ... I might be doing something wrong though and it returns false for [begin, front, start, finish, tail, start, end]
Oddly, System.out.println("Map = " + tags.stream().map(Tag::getKey).collect(Collectors.toList())); will print Map = [begin, front, start, finish, tail, start, end] which doesn't seem to be removing the duplicates - maybe it's my equals/hashcode methods?
You should call distinct after map tho remove the duplicate strings
if the list after map contains 3 or more duplicate of either "start" or "end" and not the other, using > 2 still return true.
It's Friday and it seems to show for us all 😁 accepted @HaiHoang s edit with distinct.
|
-2

What about

tags.containsAll(Arrays.asList(new Tag("start"), new Tag("stop")))

1 Comment

(Not a downvoted) The original List doesn't contain Strings, it contains a Tag object - whose implementation we don't know

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.