Java集合认为排列是否相等?

时间:2015-01-08 01:46:12

标签: java collections

我想创建可能包含重复值的集合,没有特定的顺序。

换句话说:

{ 1, 1, 2 } == { 2, 1, 1 } == { 1, 2, 1 }

事实上,我希望拥有一组这些集合,因此如果我尝试添加{ 1, 1, 2 }{ 2, 1, 1 },则第二个.add()实际上不会执行任何操作。

是否有标准集合已经表现出来?

如果我理解正确:

  • ArrayList允许重复值,但具有固定顺序
  • HashSet允许订单是任意的,但没有重复的值
  • TreeSet确保顺序是常量,但不允许重复值

我是否忽略了一个允许重复值和任意或固定顺序的集合,以便考虑两个具有相同元素的集合?


@asteri询问我的用例。在游戏中,我有不同长度的块,可以端到端铺设以填充一定距离。例如,如果距离是10,则可以用2-3-5或5-2-3或3-3-4或3-4-3或任何数量的其他排列填充。根据可用的块,我想列出所有可能解决的集合,填补空白。


CUSTOM SOLUTION
@sprinter建议创建ArrayList的子类。 @dasblinkenlight和@Dici建议使用Map存储{ Element : Count }个条目。我选择将这两个建议结合起来。下面是TreeMap的子类。密钥始终以相同的顺序存储,以确保hashCode()方法生成相同的值,例如使用相同的键和值。

我使用了increment方法,可以轻松添加特定整数"值"的新出现次数。

package com.example.treematch;

import java.util.Map;
import java.util.TreeMap;

public class TreeMatch<K> extends TreeMap<K, Integer> {

  @Override
  public boolean equals(Object other) {
    if (this == other) {
      return true;
    }

    if (!(other instanceof TreeMatch)) {
      return false;
    }

    TreeMatch otherMatch = (TreeMatch) other;
    if (size() != otherMatch.size()) {
      return false;
    }

    for (Object key : this.keySet()) {
        if (!otherMatch.containsKey(key)) {
            return false;
        }
    }

    for (Object key : otherMatch.keySet()) {
      if (!this.containsKey(key)) {
        return false;
      }

      if (this.get(key) != otherMatch.get(key)) {
        return false;
      }
    }

    return true;
  }

  public void increment(K key) {
    Integer value;

    if (this.containsKey(key)) {
      value = (this.get(key)) + 1;
    } else {
      value = 1;
    }

    this.put(key, value);
  }


  @Override
  public int hashCode() {
    int hashCode = 0;

    for (Map.Entry entry : this.entrySet()) {
      hashCode += entry.getKey().hashCode();
      hashCode = hashCode << 1;
      hashCode += entry.getValue().hashCode();
      hashCode = hashCode << 1;
    }

    return hashCode;
  }
}

6 个答案:

答案 0 :(得分:6)

Java内置库中没有任何内容,但是Guava的Multiset可以做到这一点。

答案 1 :(得分:3)

此类收集通常称为 multiset 。标准库中没有包含多个集合的实现,但外部库Guava确实包含多个集合。

答案 2 :(得分:3)

您可以使用Bag中的Eclipse Collections,也称为Multiset

MutableBag<Integer> bag1 = Bags.mutable.with(1, 1, 2);
MutableBag<Integer> bag2 = Bags.mutable.with(1, 2, 1);
Assert.assertEquals(bag1, bag2);

由于您的收藏只包含整数,因此最好使用原始包。 IntHashBagint[]数组支持,而不是Object[]数组,使用更少的内存,可能更快。

MutableIntBag intBag1 = IntBags.mutable.with(1, 1, 2);
MutableIntBag intBag2 = IntBags.mutable.with(1, 2, 1);
Assert.assertEquals(intBag1, intBag2);

注意:我是Eclipse集合的提交者。

答案 3 :(得分:2)

我认为更好的选择是以这种方式包裹Map

  • 键是你的价值
  • 值是键的出现次数
  • 基于Map.keySet()相等的相等方法,如果出现次数很重要,Map.equals()否则

答案 4 :(得分:2)

如果您将值存储为键,并且该值在地图中显示为值的次数,则

HashMap<Integer,Integer>将能够表示您的集合。例如,您的集合{1, 1, 2}将表示如下:

{{ 1 : 2 }, { 2 : 1 }}

意味着有两个项目的值为1(第一对整数),一个项目的值为2(第二对整数)。

答案 5 :(得分:1)

我建议创建一个覆盖equals方法的ArrayList子类:

class MultiMemberList extends ArrayList {
    public boolean equals(Object other) {
        if (this == other)
            return true;
        if (!(other instanceof MultiMemberList))
            return false;
        MultiMemberList otherList = (MultiMemberList)other;
        if (size() != otherList.size())
            return false;
        return stream().distinct().allMatch(element -> countElement(element) == otherList.countElement(element));
    }

    private int countElement(Object element) {
        return stream().filter(element::equals).count();
    }
}

我没有把它变得简单,但是显然你应该这样做。

您还应该覆盖哈希函数:最简单的方法是在调用超类的哈希值之前对列表进行排序。

一旦你实现了这个,那么添加了MultiMemberList对象的Set将按预期工作:根据这个定义,它不会添加第二个等于第一个的列表。