0
private List<String> values = new ArrayList<String>();

public WhitespaceEqualsTest() {
    values.add("I ");
    values.add("I");
    values.add(". ");
    values.add(".");
    values.add("1");
    values.add("1 ");

    System.out.println(refine(values));
}

private List<String> refine(List<String> input){
    ArrayList<String> outerLoopValues = (ArrayList<String>) input;
    ArrayList<String> innerLoopValues = (ArrayList<String>) input;
    ArrayList<String> results = new ArrayList<String>();

    for(String string1 : outerLoopValues){
        for(String string2 : innerLoopValues){
            if(string1.contains(string2) == false){
                results.add(string1);
            }
        }
    }

    Set<String> temp = new HashSet<String>();
    temp.addAll(results);
    results.clear();
    results.addAll(temp);

    return results;
}
@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((values == null) ? 0 : values.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    WhitespaceEqualsTest other = (WhitespaceEqualsTest) obj;
    if (values == null) {
        if (other.values != null)
            return false;
    } else if (!values.equals(other.values))
        return false;
    return true;
}

I've overriden the hashCode() and equals(), so I'm not really sure what's wrong. They are generated using Eclipse (Source -> Generate hashCode() and equals()). Why isn't it detecting that the same character without a space is contained within a character with a space? The output is:

[1, . , I , I, ., 1 ]

Alykoff Gali
  • 1,121
  • 17
  • 32
ack
  • 1,181
  • 1
  • 17
  • 36
  • 1
    Why not use ArrayList all the time instead of casting the list around? – Luca Angioloni Dec 09 '16 at 01:49
  • 1
    I somewhat doubt that you've overridden `hashCode()` and `equals(String)` for `java.lang.String`. Please provide that code. You've only provided a test that (appears) to work exactly as it should for `String`s. – CollinD Dec 09 '16 at 01:51
  • I quote @CollinD. And also assuming you did you may have broken contains which uses equals – Luca Angioloni Dec 09 '16 at 01:53
  • Your output is consistent with a broken `contains()`, because you should be getting more than 6 matches in your output. – Tim Biegeleisen Dec 09 '16 at 01:54
  • 3
    OP is throwing everything into a set, and then back into the list, so he absolutely couldn't have more than 6 matches. – CollinD Dec 09 '16 at 01:55
  • Why are you taking a given List and casting to an ArrayList? It's not good practice (you should code to the interface not the implementation) and it will cause an exception if the input List isn't an ArrayList. – Aaron Davis Dec 09 '16 at 01:58
  • Your code must match every string because for any string `s`, `s.contains(s)` is `true` and your nested loop encounters this case for every string. – Bohemian Dec 09 '16 at 01:59
  • Edited post to include overriden hashCode() and equals(). – ack Dec 09 '16 at 01:59
  • 2
    OP, you have overridden equals and hashcode for your class. Those are not called when you call `contains` on a `List`. Only `String#equals` would be used. The `List` implementation (in this case, `ArrayList`) is completely unaware of your `WhitespaceEqualsTest` class. – CollinD Dec 09 '16 at 01:59
  • 1
    String is not that easy to be overridden, see: http://stackoverflow.com/questions/10613175/override-java-string-methods – choasia Dec 09 '16 at 02:03
  • @CollinD Looks like an answer to me – OneCricketeer Dec 09 '16 at 02:05
  • @CollinD Well, how could I fix it? – ack Dec 09 '16 at 02:06
  • 2
    @AlexQuilliam don't use strings. Make a custom string wrapper and use that instead, then you can override the equals and hashCode methods – Aaron Davis Dec 09 '16 at 02:10
  • What are you trying to do in your `refine` method? You create a cartesian product of an array with itself, then for each pair in that product, you test whether the second is contained in the first. Now, no matter whether you test for contains or not contains, you are going to get the same result, which is the whole array. Because the cartesian product have a string with itself, which makes contains true, it also contains product like ("1", "I"), where the first element does not contain the second. – xiaofeng.li Dec 09 '16 at 02:33
  • You couldn't get less than 6 element either. – xiaofeng.li Dec 09 '16 at 02:36

3 Answers3

0

As mentioned in one of the comments, you should be using a String wrapper to wrap the strings and override the equals and hashcode methods.

My solution is based on the assumption that "I " should be equals to "I", hence only one of them should be added into the result.

However I'll need to addon that based on the documentation in Java Objects and Java Arraylist with regards to equals and contains implementation respectively. The hashcode method would have to return a common value. I've written the explanation in the code as comments. Let me know if there are any issues.

Main Class

public class StackOverflowMain
{
    private static List<String> values = new ArrayList<String>();

    public static void main(String[] args) {

        values.add("I ");
        values.add("I");
        values.add(". ");
        values.add(".");
        values.add("1");
        values.add("1 ");
        List<WhitespaceEqualsTest> toRefineList = new ArrayList<WhitespaceEqualsTest>();
        for (String value : values) {
            toRefineList.add(new WhitespaceEqualsTest(value));
        }

        System.out.println(refine(toRefineList));
    }

    private static List<WhitespaceEqualsTest> refine(List<WhitespaceEqualsTest> input) {
        ArrayList<WhitespaceEqualsTest> loopValues = (ArrayList<WhitespaceEqualsTest>) input;
        ArrayList<WhitespaceEqualsTest> results = new ArrayList<WhitespaceEqualsTest>();

        for (WhitespaceEqualsTest value : loopValues) {
            if (!results.contains(loopValues)) {
                results.add(value);
            }
        }

        Set<WhitespaceEqualsTest> temp = new HashSet<WhitespaceEqualsTest>();
        temp.addAll(results);
        results.clear();
        results.addAll(temp);

        return results;

    }
}

Inner WhitespaceEqualsTest Class

class WhitespaceEqualsTest {
    private String value;

    public WhitespaceEqualsTest(String value) {
        this.value = value;
    }

    public void setString(String value) {
        this.value = value;
    }

    public String getString() {
        return this.value;
    }

    public int hashCode() {
        /*
         * Arraylist.contains is evaluated by using (o==null ? e==null : o.equals(e)) as mentioned in the javadoc
         * and Object.equals() would evaluate using hashcode() first to check if the object o is equal to object e
         * before calling .equals() method to evaluate.
         * 
         * As mentioned in java doc at http://docs.oracle.com/javase/7/docs/api/java/util/Collection.html#equals(java.lang.Object)
         * c1.equals(c2) implies that c1.hashCode()==c2.hashCode() should be satisfied
         * which is not in this question
         */      
        return 0;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        WhitespaceEqualsTest other = (WhitespaceEqualsTest) obj;
        if (value == null) {
            if (other.value != null)
                return false;
        } else if (!value.contains(other.value) && !other.value.contains(value)){ 
            /*
             * Does a checking on both ends since "I " contains "I" but "I" does not contain "I " due to the whitespace
             * For this question, if one of the condition satisfy it should be equal
             */
            return false;
        }           
        return true;
    }

    @Override
    public String toString() {
        return this.value;
    }
}

Result

[I , . , 1]
Samuel Kok
  • 585
  • 8
  • 16
  • This might be what the OP intended to do. I see that you put effort into this but you do not answer the question `Why isn't it detecting that the same character without a space is contained within a character with a space?` regards – Lord_PedantenStein Dec 09 '16 at 02:58
  • Look into the equals method, I've added the comments there. For simplicity, OP used `object.equals()` in the "overridden" `equals()` which java will evaluate using `hashcode()` before evaluating using `.equals()`, in OP code, every string has a unique hashcode. Since hashcode is different, it returns false, hence, not equals. But that's not what is intended for this question, instead `hashcode()` should not return a unique value and `String.contains()` should be used in the overridden `.equals()` method to evaluate. – Samuel Kok Dec 09 '16 at 03:04
  • Yes it's awesome, but this question is about the contains method (title is contains() method not working). And `"I ".contains("I")` yields true anyway. `System.out.println("I ".contains("I"));` if you will. :) – Lord_PedantenStein Dec 09 '16 at 03:11
  • Yea, thats true, and the OP code already reflects that. As mentioned in my assumption, this solution reflects that. In OP code, there is another issue with the loop, it creates a cartesian product of the array, hence returning all 6 values as mentioned in the comments. – Samuel Kok Dec 09 '16 at 03:16
  • Of course, you can do away of all these hassle of overriding hashcode and .equals method in the string wrapper and implement the logic during the iteration itself. This code is build based on OP intent which have be used for other reasons as well. Really up to you. – Samuel Kok Dec 09 '16 at 03:26
0

String class is final. So you cannot override its equals and hashCode methods.

private List<StringWrapper> values = new ArrayList<StringWrapper>();

public WhitespaceEqualsTest() {
    values.add(new StringWrapper("I "));
    values.add(new StringWrapper("I"));
    values.add(new StringWrapper(". "));
    values.add(new StringWrapper("."));
    values.add(new StringWrapper("1"));
    values.add(new StringWrapper("1 "));

    System.out.println(refine(values));
}

private List<StringWrapper> refine(List<StringWrapper> input){
    //no need to iterate the list
    //the set will automatically cancel out the duplicate
    Set<StringWrapper> temp = new HashSet<StringWrapper>(input);

    ArrayList<StringWrapper> results = new ArrayList<StringWrapper>();
    results.addAll(temp);
    return results;
}

Create a wrapper class of String then override the equals and hashcode method.

class StringWrapper {
    private String value;

    public StringWrapper(String value){
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
    @Override
    public String toString(){
        return value;
    }
    @Override
    public boolean equals(Object obj){
        boolean result = Boolean.FALSE;
        if(obj != null && obj instanceof StringWrapper){
            StringWrapper stringWrapper = (StringWrapper) obj;
            result = value.trim().equals(stringWrapper.getValue().trim());
        }
        return result;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((value.trim() == null) ? 0 : value.trim().hashCode());
        return result;
    }
}
Noel
  • 91
  • 3
-1

You add the values to a Set. In a Set in any case a value occurs once - hence it is a set. ;)

You might as well modify the loop to see what happens

for(String string1 : outerLoopValues){
            for(String string2 : innerLoopValues){
                if(string1.contains(string2) == false){
                    results.add(string1);
                    System.out.println("added \"" + string1 + "\" since it does not contain \"" + string2 + "\"");
                }
            }
        }

Giving the following output:

added "I " since it does  not contain ". "
added "I " since it does  not contain "."
added "I " since it does  not contain "1"
added "I " since it does  not contain "1 "
added "I" since it does  not contain "I "
added "I" since it does  not contain ". "
added "I" since it does  not contain "."
added "I" since it does  not contain "1"
added "I" since it does  not contain "1 "
......
[1, . , I , I, ., 1 ]

To add them if they do not contain each other is the idea i guess?

Then pushing the List through a Set removes the duplicates! See here: Does adding a duplicate value to a HashSet/HashMap replace the previous value

Changing the condition in the Loop from false to true yields this (no change after using the Set/HashSet in the last line of output!)

added "I " since it does contain "I "
added "I " since it does contain "I"
added "I" since it does contain "I"
added ". " since it does contain ". "
added ". " since it does contain "."
added "." since it does contain "."
added "1" since it does contain "1"
added "1 " since it does contain "1"
added "1 " since it does contain "1 "
[1, . , I , I, ., 1 ]

Which answers your question: It does detect if e.g. "I " contains "I".

System.out.println("I ".contains("I"));

says "true"

Hope this helps ^^-d

Community
  • 1
  • 1
  • Well, if that were the case wouldn't there only be 3 values, if it was working correctly? – ack Dec 09 '16 at 02:09
  • Nope, your 6 values are "unique" at least in the part of code where the set comes into play. In the Set "I" is not "I " and "." is not ". ". http://stackoverflow.com/questions/12940663/does-adding-a-duplicate-value-to-a-hashset-hashmap-replace-the-previous-value – Lord_PedantenStein Dec 09 '16 at 02:12
  • The set is not the issue here, try running the code without the set snippet. – ack Dec 09 '16 at 02:14
  • Set a breakpoint at `Set temp ` (where there are multiples of the inputstrings in the result List) and breakpoint at `return results;` where every input string is just contained once. – Lord_PedantenStein Dec 09 '16 at 02:22
  • @AlexQuilliam check the output if the condition is changed from false to true. (second output in this answer) ^^ – Lord_PedantenStein Dec 09 '16 at 02:46
  • So to answer your question "Why isn't it detecting that the same character without a space is contained within a character with a space?" - well it does (see case with condition == true) ^^ – Lord_PedantenStein Dec 09 '16 at 02:53