如何从Java ArrayList中删除重复的对象?

时间:2012-06-27 18:36:31

标签: java object collections arraylist duplicates

我需要你的一些示例代码帮助我找不到的情况。 我有一个简单的对象列表。我的班级是这样的:

class MyClass {
    String str;
    Integer intgr;
}

该列表包含以下元素:

[{a1  5}, {b2  3}, {g1  1}, {b5  1}, {c9  11}, {g2  3}, {d1  4}, {b3  19}... ... ...]

我需要检查字符串中是否有任何元素包含相同的前缀(这里后缀是最后一个字符)然后保留该元素在整数中具有更大的值。上面示例列表的预期输出将是:

[{a1  5}, {c9  11}, {g2  3}, {d1  4}, {b3  19}... ... ...]

字符串将具有唯一值,但可以在前缀中匹配。我在java中不是那么好。所以有人可以帮我解决这个问题吗?这是我正在尝试的代码,但获得IndexOutOfBoundsException。此代码有错误,因此需要您的帮助。

谢谢!

        int size = list.size();
        for (int j = 0; j < size; j++) {
        if (list.get(j).str.substring(0, list.get(j).str.length()-1).compareTo(list.get(j+1).str.substring(0, list.get(j+1).str.length()-1)) == 0) {
            if (list.get(j).intgr > list.get(j+1).intgr)
                list.remove(list.get(j+1));
                size--;
            else {
                list.remove(list.get(j));
                j--;
                size--;
            }
        }
    }

4 个答案:

答案 0 :(得分:1)

您可以迭代您的元素集合,将它们添加到Map中,将键(前缀)与值(对象)相关联。每次添加元素时,都要检查使用相同前缀存储的元素是否大于添加的元素。

为了有这种行为:

provides this: [{a1 5}, {b2 3}, {g1 1}, {b5 1}, {c9 11}, {g2 3}, {d1 4}, {b3 19}]
results this: [{a1 5}, {c9 11}, {g2 3}, {d1 4}, {b3 19}]

您可以实现以下内容:

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class TestSystemOut {

    public static void main(final String[] a) {
        List<MyObj> list = prepareList();
        System.out.println("provides this: " + list);

        Map<String, MyObj> map = new LinkedHashMap<String, MyObj>(); // if result order doesn't matter this can be a simple HashMap

        String strTmp;
        for (MyObj obj : list) {

            strTmp = obj.str;
            strTmp = strTmp.substring(0, strTmp.length() - 1);

            if (map.get(strTmp) == null || map.get(strTmp).integer < obj.integer) {
                map.remove(strTmp); // this could be removed if order of result doesn't matter
                map.put(strTmp, obj);
            }
        }

        list.clear();
        list.addAll(map.values());

        System.out.println("results this: " + list);
    }

    public static class MyObj {
        String str;
        Integer integer;

        public MyObj(final String str, final Integer integer) {
            super();
            this.str = str;
            this.integer = integer;
        }

        @Override
        public String toString() {
            return "{" + str + " " + integer + "}";
        }

    }

    private static List<MyObj> prepareList() {
        List<MyObj> list = new ArrayList<MyObj>();
        list.add(new MyObj("a1", 5));
        list.add(new MyObj("b2", 3));
        list.add(new MyObj("g1", 1));
        list.add(new MyObj("b5", 1));
        list.add(new MyObj("c9", 11));
        list.add(new MyObj("g2", 3));
        list.add(new MyObj("d1", 4));
        list.add(new MyObj("b3", 19));
        return list;
    }
}

答案 1 :(得分:0)

另一种(可能更容易阅读和调试)的方式是:

  1. 将您的商品放入列表
  2. 遍历列表并填充一个Map,其中String是字母,MyClass是最小值(即如果地图已经包含&#34; a&#34;,请检查您的新MyClass是否更大或更小并替换相应地)
  3. 简单实施:

    public class Test {
    
        public static void main(String[] args) throws InterruptedException {
            List<MyClass> list = Arrays.asList(new MyClass("a1", 5),
                    new MyClass("b2",  3),
                    new MyClass("g1",  1),
                    new MyClass("b5",  1),
                    new MyClass("c9",  11),
                    new MyClass("g2",  3),
                    new MyClass("d1",  4),
                    new MyClass("b3",  19));
    
            Map<String, MyClass> map = new HashMap<String, MyClass>();
    
            for (MyClass mc : list) {
                MyClass current = map.get(mc.getLetter());
                if (current == null || mc.intgr > current.intgr) {
                    map.put(mc.getLetter(), mc);
                }
            }
            System.out.println(map);
        }
    
        static class MyClass  {
    
            String str;
            Integer intgr;
    
            MyClass(String str, Integer intgr) {
                this.str = str;
                this.intgr = intgr;
            }
            String getLetter() {
                return str.substring(0,1);
            }
    
            @Override
            public String toString() {
                return "[" + str + " " + intgr + "]";
            }
        }
    }
    

答案 2 :(得分:0)

/*
 * For every string in the list, look at all strings in the
 * list and compare the substring from 0 to length-1, if they
 * match and the id is not the same as the current s (i.e. s and
 * list.get(i) are at the same address) then remove that string.
 */
public ArrayList remdup(ArrayList<String> list) {

    for (String s : list) {
        for (int i=0; i<list.size();i++) {
            if (s.substring(0, s.length()-1).compareTo(list.get(i).substring(0, list.get(i).length()-1)) == 0
                    && list.indexOf(list.get(i)) != list.indexOf(s)) {
                list.remove(list.get(i));
            }
        }
    }
    return list;
}

也许尝试一下,尚未测试,但认为它应该工作。如果它不能尝试使用与compareTo()不同的东西,可以使用equals()作为替代。

此外,您将获得IndexOutOfBoundsException,因为每次删除元素时都会减去大小,从而缩小搜索范围。你没有考虑到remove()会为你做这件事。

我会考虑使用Map来代替类。这样你就不必重新发明轮子了。

答案 3 :(得分:0)

您的代码存在两个问题。首先,当j == size - 1(最后一次迭代)时,您正在调用list.get(j + 1),这是导致异常的原因。只需将循环条件更改为j < size - 1,异常就会消失。 (或者,从j = 1开始,并与前一个元素进行比较。)

其次,您只是将每个元素与其直接后继元素进行比较。根据你的描述,这听起来不像你想要做的那样。

我建议在单独的方法中捕获比较逻辑。它可能是MyClass的一部分:

class MyClass {
    String str;
    Integer intgr;
    /**
     * Returns the relationship between this and another MyClass instance.
     * The relationship can be one of three values:
     * <pre>
     *   -1 - This object should be discarded and other kept
     *    0 - There is no relationship between this and other
     *    1 - This object should be kept and other discarded
     * </pre>
     *
     * @param other The other instance to evaluate
     *
     * @return 0 if there is no relationship.
     */
    public int relationTo(MyClass other) {
        final String myPrefix = str.substring(0, str.length() - 1);
        final String otherPrefix = other.str.substring(0, other.str.length() - 1);
        if (myPrefix.equals(otherPrefix)) {
            return intgr < other.intgr ? -1 : 1;
        } else {
            return 0;
        }
    }
}

(这很容易转换为一个带有两个参数的方法,这个参数可以在MyClass之外。)然后你可以使用这个方法来决定保留什么。您需要进行双重迭代才能找到不相邻的对象:

int size = list.size();
for (int i = 0; i < size; ++i) {
    final MyClass current = list.get(i);
    for (int j = 0; j < i; ++j) {
        final MyClass previous = list.get(j);
        final int relation = previous.relationTo(current);
        if (relation < 0) {
            // remove previous (at index j)
            list.remove(j);
            --i;
            --j;
            --size;
        } else if (relation > 0) {
            // remove current (at index i)
            list.remove(i);
            --i;
            --size;
            break; // exit inner loop
        }
        // else current and previous don't share a prefix
    }
}