【问题标题】:How can I distinguish the same integer object?如何区分相同的整数对象?
【发布时间】:2015-10-05 10:47:38
【问题描述】:

众所周知,java 缓存从 -128 到 127 的整数。现在,我有一个类容器,如下所示:

public class Container<T>{
    private List<T> elements;

    public boolean add(T t){
        //add the element to the container

        //if elements already contains t such that 
        //there is e from elements so e == t
        //the IllegalArgumentException should be thrown
    }
}

现在,当我尝试创建 Container&lt;Integer&gt; container 然后像这样添加一些内容时

container.add(1);
container.add(2);
container.add(1); //here the IllegalArgumentException is being thrown.

我得到了例外。 这不是我想要的行为。我需要为添加到容器中的任何整数创建不同的对象。我该怎么做?

【问题讨论】:

  • 你试过显式使用new Integer(123)吗?
  • 所以你实际上是在创建一个损坏的Set。为什么要打扰?
  • 根据您的容器的实现,这 正确的行为。另一种选择是删除容器中的“throw-on-same”检查。无论哪种方式,您为什么不坚持使用同样不允许重复的标准 Java java.util.Set
  • @St.Antario 您希望Container 中有不同的元素,这正是Sets 所做的。但是Set 定义明确,因此它不会在添加重复项时抛出异常,它根本不会添加它。当然,它以不同的方式定义了 2 个对象的相同性(equals/hashcodeTreeSet 的情况下,compareTo)。由于您编写课程的方式,您别无选择,只能在插入值时使用 new Integer(1)
  • 基本上,下定决心。你想要一个允许重复的容器吗?如果您想要一个不允许重复的容器,那么您不能将1 放在那里两次。如果你想要一个允许重复的容器,那么你不能使用== 来排除元素。但是您似乎想要一个既允许又不允许重复的容器。不要那样做。你的代码很容易出错,没人会理解。

标签: java list integer


【解决方案1】:

使用new 运算符显式创建Integer 的新实例。

container.add(new Integer(1));
container.add(new Integer(2));
container.add(new Integer(1));

【讨论】:

  • 这不是解决方案。正如我在评论中所写,这是违反直觉的,因此会出现错误。
  • 我正在研究另一种方法。无论哪种情况,您都将实例化一个新的 Integer 对象以击败 IntCache
  • “我不喜欢它”不会使它成为错误的答案。如果你想要一个新对象,你必须创建一个新对象。 new 关键字是最简单、最可靠的方法。
  • @yshavit 这不仅仅是“我不喜欢”。如果除我之外的其他人尝试使用它,他们有 99.9999% 的可能性会引入错误。
  • @St.Antario 您真的打算将其用于他人吗?这个IdentityHashSet 的用例是什么?
【解决方案2】:

如果您真的想对Integers 有特殊行为,您可以进行检查并手动创建一个新实例:

public boolean add(T t) {
    if (t instanceof Integer) {
        t = new Integer(((Integer)t).intValue());
    }

    // The rest of the code.
}

请记住,如果您也想对其他原语执行此操作,这将会变得更加混乱。

【讨论】:

  • 对于字符串,由于字符串常量的实习
  • @yshavit 这个问题只问了整数,但是是的;如果 OP 想将其扩展到每种类型,那么这将很快变得非常混乱。
【解决方案3】:

您在 cmets 中声明,使用它的一些开发人员会将此称为错误。但正如Boxing Conversations in the Java Language Specification所述:

如果被装箱的值 p 是 int 类型的整数文字,介于 -128 和 127 之间(第 3.10.1 节),或者布尔文字 true 或 false(第 3.10.3 节),或者是 '\ 之间的字符文字u0000' 和 '\u007f' (第 3.10.4 节),则设 a 和 b 是 p 的任意两次装箱转换的结果。 a == b 总是如此。

会有一个 VM-Property 来更改 IntegerCache (-Djava.lang.Integer.IntegerCache.high) 的大小,但您不能指定任何低于 127 的值或禁用它。在 IntegerCache 类中可以看到:assert IntegerCache.high &gt;= 127;

所以使用new Integer() 而不是自动装箱是唯一正确的方法。我不会坚持使用 instanceof 和创建新对象的其他 cmets 中指出的解决方案(正如他们所说的那样!),因为这些既不干净也不正确。

请再看看您的架构。如果一个元素要被添加两次,你真的想抛出一个异常吗?为什么不使用普通的java.util.Set 并处理它的布尔返回值?这将是执行此操作的最标准的 Java 方式。不要重新发明轮子,坚持现有的、经过良好测试和明确定义的类。

【讨论】:

  • 为什么不使用普通的 java.util.Set :因为 Set 将始终认为两个 Integer 对象相等,无论它们是否指向相同缓存值与否。这不是OP想要的。 OP 的目标是身份而非平等。
【解决方案4】:

如果TNumber,为什么不允许重复。

public class Container<T>{
    private List<T> elements;

    public boolean add(T t){
        //add the element to the container

        // <<<IF <T> IS NOT NUMBER>>> and if elements already contains t such that 
        //there is e from elements so e == t
        //the IllegalArgumentException should be thrown
    }
}

【讨论】:

    【解决方案5】:

    如果您可以稍微更改一下公共 API,我会这样做。我会创建一个子类:

    public class IntegerContainer extends Container<Integer> {
        public boolean add(Integer integer) {
            return super.add(new Integer(integer));
        }
    }
    

    然后我将 Container 的构造函数设置为 protected 并添加一个静态工厂获取一个类:

    public class Container<T>  {
        protected Container() {}
    
        public static <T> Container<T> of(Class<T> type) {
            if (type.isAssignableFrom(Integer.class)) {
                return (Container<T>) new IntegerContainer();
            }
            return new Container<T>();
        }
    
        private List<T> elements = new ArrayList<T>();
    
        public boolean add(T t){
            for (T e : elements) {
                if (t == e)
                    throw new IllegalArgumentException();
            }
            return elements.add(t);
        }
    
        public static void main(String args[]) {
            Container<Integer> integerContainer = Container.of(Integer.class);
            integerContainer.add(1);
            integerContainer.add(2);
            integerContainer.add(1); // Doesn't throw
    
            Container<String> stringContainer = Container.of(String.class);
            String same = "hi";
            stringContainer.add(same);
            stringContainer.add(same); // Throws
            return;
        }
    }
    

    请注意,这会改变 API 用户创建 Containers 的方式;他们必须使用他们想要的类型调用Container.of

    【讨论】:

      【解决方案6】:

      我不知道你想做什么,但这不是这样做的方法。

      Integer 是一个不可变的类。因此,任何您认为拥有两个对象xy 以使(x!=y &amp;&amp; x.equals(y)) 没有正确使用该类的情况。它是允许的,但绝不应该有用或不受欢迎。

      这就是为什么允许(但不是必须)从对valueOf(.) 的重复(显式或隐式)调用中返回相同对象的原因。

      【讨论】:

        猜你喜欢
        • 2013-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-12-05
        • 1970-01-01
        • 2010-10-13
        • 2018-07-05
        • 2015-06-13
        相关资源
        最近更新 更多