 <p>The general strategy here is that you want to <em>maintain a context</em> as you traverse the list, and at each step, you use that piece of context to answer the question of whether the current item should be kept or thrown out. In pseudo-code:</p> <pre><code>public static &lt;A&gt; List&lt;A&gt; removeDuplicates(List&lt;? extends A&gt; original) { List&lt;A&gt; result = new ArrayList&lt;A&gt;(); /* initialize context */ for (A item : original) { if ( /* context says item is not a duplicate */ ) { result.add(item); } /* update context to incorporate the current `item` */ } return result; } </code></pre> <p>Some people have brought up the question of whether you mean consecutive duplicates or non-consecutive ones. In reality, the difference in the solutions is small:</p> <ol> <li>For consecutive duplicates the context is the most recently seen item.</li> <li>For non-consecutive it's the <code>Set&lt;A&gt;</code> of all items seen up to that point.</li> </ol> <p>I'll let you fill in the pattern for those cases.</p>