29

我想知道为什么 HashSet、LinkedHashSet 和 TreeSet 的实现不允许空元素?每当我尝试运行以下代码时,它都会引发空指针异常。

public static void main(String[] args) {

    HashSet<Integer> hashSet = new HashSet<Integer>();

    hashSet.add(2);
    hashSet.add(5);
    hashSet.add(1);
//  hashSet.add(null);  will throw null pointer 
    hashSet.add(999);
    hashSet.add(10);
    hashSet.add(10);
    hashSet.add(11);
    hashSet.add(9);
    hashSet.add(10);
    hashSet.add(000);
    hashSet.add(999);
    hashSet.add(0);

    Iterator<Integer> it = hashSet.iterator();
    while(it.hasNext()){
        int i = it.next();
        System.out.print(i+" ");
    }
    }

请指导我。

4

11 回答 11

64

这就是我不喜欢依赖自动装箱的原因。Java Collections 不能存储原语(因为你需要像Trove这样的第三方 API )。所以,真的,当你执行这样的代码时:

hashSet.add(2);
hashSet.add(5);

真正发生的是:

hashSet.add(new Integer(2));
hashSet.add(new Integer(5));

向哈希集添加 null不是问题,这部分工作得很好。当您尝试将您的值拆箱为原始 int 时,您的 NPE 稍后会出现:

while(it.hasNext()){
    int i = it.next();
    System.out.print(i+" ");
}

null遇到该值时,JVM 会尝试将其拆箱为 int 原语,这会导致 NPE。您应该更改代码以避免这种情况:

while(it.hasNext()){
    final Integer i = it.next();
    System.out.print(i+" ");
}
于 2013-02-08T15:43:39.147 回答
21

1)你确定你得到编译时错误吗?我不这么认为,我猜代码在运行时会抛出 NPE

int i = it.next();

2)事实上java.util.Set接口并不禁止空元素,一些JCF Set实现也允许空元素:

设置 API -A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element.

哈希集 API - This class permits the null element.

LinkedHashSet API -This class provides all of the optional Set operations, and permits null elements

TreeSet.add API -throws NullPointerException - if the specified element is null and this set uses natural ordering, or its comparator does not permit null elements

于 2013-02-08T14:50:29.160 回答
8

不,设置接口只允许空值它的实现,即TreeSet不允许空值。

即使您没有编写迭代代码并且仅oTreeSet.add(null)在您的代码中编译并且在运行时它会抛出 NullPointerException。

TreeSet类的add()方法内部调用put()TreeMap 类的方法。 nullput() 方法中的值不允许如下代码所示

if (key == null)
     throw new NullPointerException();
于 2016-10-20T14:34:32.020 回答
5

接口的重点Set是使用有关元素的信息(哈希码或比较)来加快实现速度。

null没有那个信息。

于 2013-02-08T14:29:42.120 回答
4

Set Interface 不允许 null 因为在 TreeSet 中,它按排序顺序存储元素,所以每次我们添加新元素时,它都会比较值然后排序。所以在内部发生的事情是将新添加的空值与现有值进行比较,因此它将抛出 NullPointerException。

String str=null;
if(str.equals("abc"))
{
}
//it will throw null pointer exception

这就是为什么它不允许空值。

于 2016-10-06T05:26:51.610 回答
1

Set 允许添加 null 所以这不是问题。其次Java程序需要先编译成字节码再执行。NullPointerException是在运行时抛出的异常。编译时应该不是问题。现在让我们分析一下为什么会得到 NPE。

Iterator这里应该输出类型的对象,Integer我们希望将结果存储在primitive type int变量中。Integer 是一个类,它可以具有其类型的引用来引用 null,但原语不能保存 null 值。

Iterator<Integer> it = hashSet.iterator(); // Iterator of Type Integer
while(it.hasNext()){
    int i = it.next(); // it.next outputs Integer, but result is tried to be held in a primitive type variable
    System.out.print(i+" ");
}

int i = it.next();被执行时,然后public int intValue()被调用以将整数对象转换为原始 int。当it.next()返回 null 时,null.intValue()执行结果为NullPointerException.

如果使用 Integer 而不是 int 则不会有异常

Integer i = it.next();
于 2016-04-10T16:58:33.870 回答
1

你提到过:

// hashSet.add(null); 将抛出空指针

这是不正确的,因为 aHashSet允许将 anull添加到其中,因此,您不会遇到此语句的任何编译失败或运行时异常。但是,什么时候会执行下面的语句

int i = it.next();

你会得到一个运行时异常,如下所示:

线程“main”中的异常 java.lang.NullPointerException:无法调用“java.lang.Integer.intValue()”,因为“java.util.Iterator.next()”的返回值在 Main.main(Main.爪哇:24)

例外是不言自明的。JLS §5.1.8规定如下:

如果 r 为 null,则拆箱转换将引发 NullPointerException

下面给出了另一个理解这个概念的例子:

public class Main {
    public static void main(String[] args) {
        Integer x = null;
        print(x);
    }

    static void print(int x) {
        System.out.println(x);
    }
}

输出:

线程“main”java.lang.NullPointerException 中的异常:无法调用“java.lang.Integer.intValue()”,因为 Main.main(Main.java:7) 处的“x”为空

事实上,编译器会提前警告你:

空指针访问:这个 Integer 类型的表达式为空,但需要自动拆箱

于 2021-03-27T17:50:23.113 回答
0
public class JavaHashSetTest {


    public static void main(String[] args) {
        Set<Integer> hashset= new HashSet<Integer>();

        hashset.add(null);
        hashset.add(22);
        hashset.add(222);
        hashset.add(null);
        hashset.add(11);
        hashset.add(233);
        //  TreeSet<String> tset=hashset;

      Iterator<Integer> it = hashset.iterator();
        while(it.hasNext()){
            Integer i = it.next();
            System.out.print(i+" ");
        }
    }

}

我的代码正在工作,为什么它会给你 Nullpointer 我试图对包含 Null 值的哈希集进行排序,然后它会给你异常,否则它工作正常。

于 2015-05-28T09:37:44.593 回答
0

只有在编译时TreeSet才会获得运行时Null pointer Exception并且HashSet不会出现任何错误,并且运行时它只会允许一个null值,如果我们输入的值超过这个值,它将覆盖HashSet. 看这里:

public class HasSetDemo {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //no error it will print only one null and 2,5,8
        Set<Integer> s = new HashSet<Integer>();
        s.add(2);
        s.add(5);
        s.add(8);
        s.add(null);
        s.add(null);//it will override and hashset will allow one null value
        Iterator i=s.iterator();        
        while(i.hasNext()){
            Object in=(Object)i.next();
            System.out.println(" hi "+in);
        }
    }
}

输出

hi null
hi 2
hi 5
hi 8

案例二:

public class SetDemo {

    public static void main(String[] args) {
        // either its may be string or integer it will give NullPointerException in run time
        Set<String> s2 = new TreeSet<>();
        s2.add("ramana");
        s2.add(null);
        s2.add("4");
        s2.add("5");
        s2.add(null);
        Iterator<String> i=s2.iterator();       
        while(i.hasNext()){
            System.out.println(i.next());
        }
    }
}

输出:

Exception in thread "main" java.lang.NullPointerException
    at java.util.TreeMap.put(Unknown Source)
    at java.util.TreeSet.add(Unknown Source)
    at com.sagili.set.SetDemo.main(SetDemo.java:13)
于 2019-01-24T06:34:59.823 回答
-1

将 null 添加到像 HashSet Arraylist 这样的集合只会在集合用于排序时产生问题。除此之外,null 将适用于迭代器和正常场景以显示列表或集合的内容。

于 2016-04-22T15:03:04.023 回答
-1

Set 接口内部使用 HashMap 实现类。每当我们使用 add() 时,我们提供的值作为值的键存储到 Map 中,它会创建一个空对象。

因此,地图不允许重复。

于 2016-07-03T03:23:54.133 回答