Java的int -128到127的问题与解决办法
Posted 从零开始的智障生活
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java的int -128到127的问题与解决办法相关的知识,希望对你有一定的参考价值。
目录
一、问题描述
写了个实现单链表的代码,其中有一条按值删除,测试单链表插入int 1-1000,删除方法接受的参数是Object类型,但是发现删除的时候,只能删除到127。到128就石沉大海了。
下面是单链表结点数据类型:
public class SingleListNode {
private Object element;
private SingleListNode next;
public SingleListNode(Object it,SingleListNode nextval) {element = it;next = nextval;}
public SingleListNode(SingleListNode nextval) {next=nextval;}
public Object getElement() {return element;}
public void setElement(Object it) {this.element = it;}
public SingleListNode getNext() {return next;}
public void setNext(SingleListNode nextval) {this.next = nextval;}
}
单链表数据类型,里面有插入和删除的功能:
public class SLList implements LinkedList {
private SingleListNode head;
public SLList() {
head=new SingleListNode(null);
}
public void inserttoHead(Object item) {
// 将头结点的子节点设置为新节点的子节点
// 将新节点作为头结点的子节点
head.setNext(new SingleListNode(item,head.getNext()));
}
public void removebyValue(Object item) {
// System.out.println(item);
/**
* 用一个游标来记录当前节点的父节点
*/
if(isEmpty()==true)return;
SingleListNode curNode = head.getNext();
SingleListNode curNode_father = head;
if((int)item>127)System.out.println(curNode.getElement());
/**
* 当传入的item是int,且>127时,如128,我们发现即使curNode.getElement()的值是128那也不会被判定为相等
*
*/
while((curNode!=null)&&(curNode.getElement()!=item)) {
if((int)item>127) {System.out.println(curNode.getElement());}
curNode_father = curNode;
curNode = curNode.getNext();
}
if((curNode!=null)&&(curNode.getElement()==item)) {
curNode_father.setNext(curNode.getNext());
System.out.println(item);
}
}
}
测试代码:
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
SLList sll = new SLList();
long startnsTime = System.nanoTime();//精确到纳秒
for(int i=1000;i>0;i--) {
sll.inserttoHead(i); //单链表顺序 1、。。。、1000
}
long endnsTime = System.nanoTime();//精确到纳秒
System.out.println("插入到头1~1000时间:"+(endnsTime-startnsTime)+"ns");
int i=0;
long startTime_removebyvalue_allfromhead = System.nanoTime();
while((i++)<1001) {
sll.removebyValue(i);// 删除顺序 1、...、1000
}
long endTime_removebyvalue_allfromhead = System.nanoTime();
System.out.println("按值从头删除:"+(endTime_removebyvalue_allfromhead-startTime_removebyvalue_allfromhead)+"ns");}
}
运行过程中发现只能删除到127,从128开始无法删除,经过测试,是从removebyValue方法中的删除代码的这段命令开始变化:
curNode.getElement()!=item
就是超过127的int值不能与被赋予同样int值的Object再等值了。
二、问题解析
int是一种基本数据类型,是原语。不会作为对象来处理。
public class test {
public static void main(String[] args) {
Object it = new Object(); // Object类型
int i=127; // int 基本数据类型
it=i; // 看下赋值后类型变化
System.out.println("it类型:"+it.getClass().getName()+"\\tit的值:"+it); // 结果是:it类型:java.lang.Integer
// 然后来测试下是否相等
// 已知it已经是Integer类型
if(it==(Object)i) {
System.out.println("it类型:"+it.getClass().getName()+"\\t(Object)i类型:"+((Object)i).getClass().getName());
}
if((int)it==i) {
System.out.println(i);
}
}
}
当 i 的值是127 输出:
it类型:java.lang.Integer it的值:127
it类型:java.lang.Integer (Object)i类型:java.lang.Integer
127
当 i 的值是128 输出:
it类型:java.lang.Integer it的值:128
128
结果分析:
2.1 类型解析
int 赋值给 Object,Object会被转换成Integer类型,故 it=i 此时 it 是Integer类型。基本数据类型是不能直接赋值给对象类型的。int是基本数据类型,有两种方式,可以使两者相等:
- int类型转换成int的包装类java.lang.Integer。在赋值之后it会被修改为Integer类型;
it==(Object)i
- Integer转换成it类型,但其实是Integer拆箱成int类型。
(int)it==i
也就是说在【问题描述】的单链表中,在TestSLL测试单链表功能时插入的时候,是int类型值传进单链表,而单链表的接口是Object类型,也就是插入后是作为Integer类型存储。
然后在按值删除的时候,传入的还是int类型,接口是Object类型,也就是传入后是成为了Integer类型。
2.2 包装局限
可以发现,在类test中,当 i 取值128时, it==(Object)i 为false,这就要涉及到Java自身的限制:自动装箱。
Object it = new Object();
int i = 127;
it = i;
上面对 it 的赋值过程等价于:
Integer it = 127;
而 i 必须要经历隐式强制类型转换成Integer才行。
而这个过程属于Java对基本数据类型 int 的自动装箱。虽然我们习惯了使用基本数据类型,但是在面向对象语言中,最正统的还是对象类型数据。
所以,当单独使用 int i = 127的时候,不会管它,i 依然是基本数据类型。但是当其与对象进行接触,就会优先被转换成对象类型 java.lang.Integer。
- 两个分别定义的相同基本数据类型,如 int,如果值相等,那么==的话肯定是true;
- 两个分别定义的对象类型类型,如Integer,虽然值相等,但两者不相等,因为==指的是完全相等,但是由于两个对象是分别定义的,所以地址不一样,故不相等。
public class test(){
public static void main(String[] args) {
int i1=1; // 判断基本数据类型是否相等
int i2=1;
if(i1==i2)System.out.println("int==int");
// 判断同值对象类型是否相等
ArrayList al1 = new ArrayList();
ArrayList al2 = new ArrayList();
if(al1==al2)System.out.println("ArrayList==ArrayList");
else System.out.println("ArrayList!=ArrayList");
// Java针对基本数据类型的包装类的特殊处理
Integer j1 = 127;
Integer j2 = 127;
if(j1==j2)System.out.println("Integer<128==Integer<128");
// Java力所不能及了
Integer k1 = 128;
Integer k2 = 128;
if(k1==k2)System.out.println("k1==k2");
else System.out.println("Integer>127!=Integer>127");
}
}
运行结果:
int==int
ArrayList!=ArrayList
Integer<127==Integer<127
Integer>128!=Integer>128
从上面代码中可以发现,虽然Integer是属于对象类型,但是当Integer赋值<=127时,只要值相等,则二者相等,当Integer赋值>=128时,二者不可能相等。为了一定程度上解决基本数据类型与对象类型的矛盾,Java 使用了一种缓存机制,但是仅限于 int 值 属于 -128到127时。
- 当int为基本数据类型时,值相等,可以一直相等;
- 当int需要转换成对象类型时,目标对象会默认用一个Integer类型来代替,例如:
int i = 1; Object it = new Object(); it = i; /** * 因为这里int要被转换成Object。目标对象it会自动向下转型成 Integer类型。 * Object it = new Object();it = i;可以用Integer it = i; 即 Integer it = 1。 * 出于对基本数据类型的自动装箱:编译时会执行Integer it = new Integer(1); */
- 当Integer类型判断是否相等时,如果Integer的值属于-128到127,会被判断属于IntegerCache。
IntegerCache是在Java编译时被同时编译的,其功能是先缓存-128到127,当创建一个Integer对象时,如果Integer的值属于-128到127,就会把这个对象的地址设定在这个缓存的位置上。从而可以使Integer对象类型同值相等。
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
2.3 解决办法
但是这样就导致了,当int或Integer不在-128到127的范围时,无法用==来判断其对象相等。由于为了方便,类的接口通常给的类型都是Object,即最上层类型。那么当我们传进去的是整数时,怎么让其判断相等?也就是怎么让两个对象值相等判断判断?
由于==是判断两个对象的地址是否相等。所以Java提供了一个概念上的判断对象的方法名:equals()。这个方法只是概念上的。需要数据类型定义这个方法。如我创建的单链表,单链表结点需要自己覆写一个方法equals()和hashCode()方法。可以总结为:
- 如果用的数据类型,没有自定义的equals()方法,那么如果判断相等,就相当于==,即判断地址相等;
- 如果用的数据类型,有自定义的【a.equals(b)】方法,即当对象a和对象b的数据域相等时,返回true。则可以避免使用==判断地址相等。
类Object就有一个equals()方法。
比如 String 是对象类型,覆写了equals()方法,就可以避免使用==。
Integer同样覆写了equals()方法。
接口List就有一个equals()方法。
覆写equals()方法有一个常见步骤。也可以自动生成,在eclipse中,在结点类中右键——》source——》Generate hashCode() and equals()。
import java.util.Objects;
public class SingleListNode {
private Object element;
private SingleListNode next;
public SingleListNode(Object it,SingleListNode nextval) {element = it;next = nextval;}
public SingleListNode(SingleListNode nextval) {next=nextval;}
public Object getElement() {return element;}
public void setElement(Object it) {this.element = it;}
public SingleListNode getNext() {return next;}
public void setNext(SingleListNode nextval) {this.next = nextval;}
@Override
public int hashCode() {
return Objects.hash(element, next);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SingleListNode other = (SingleListNode) obj;
return Objects.equals(element, other.element) && Objects.equals(next, other.next);
}
}
判断是否与item相等用equals()实现。curNdde.getElement()==item用以下语句代替。
curNode.equals(new SingleListNode(item,curNode.getNext()))
public class SLList implements LinkedList {
private SingleListNode head;
public SLList() {
head=new SingleListNode(null);
}
public void inserttoHead(Object item) {
// 将头结点的子节点设置为新节点的子节点
// 将新节点作为头结点的子节点
head.setNext(new SingleListNode(item,head.getNext()));
}
public void removebyValue(Object item) {
/**
* 用一个游标来记录当前节点的父节点
*/
if(isEmpty()==true)return;
SingleListNode curNode = head.getNext();
SingleListNode curNode_father = head;
while((curNode!=null)&&(curNode.equals(new SingleListNode(item,curNode.getNext()))==false)) {
curNode_father = curNode;
curNode = curNode.getNext();
}
if((curNode!=null)&&(curNode.equals(new SingleListNode(item,curNode.getNext())))) {
curNode_father.setNext(curNode.getNext());
System.out.println(item);
}
}
}
测试代码:
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
SLList sll = new SLList();
long startnsTime = System.nanoTime();//精确到纳秒
for(int i=1000;i>0;i--) {
sll.inserttoHead(i); //单链表顺序 1、。。。、1000
}
long endnsTime = System.nanoTime();//精确到纳秒
System.out.println("插入到头1~1000时间:"+(endnsTime-startnsTime)+"ns");
int i=0;
long startTime_removebyvalue_allfromhead = System.nanoTime();
while((i++)<1001) {
sll.removebyValue(i);// 删除顺序 1、...、1000
}
long endTime_removebyvalue_allfromhead = System.nanoTime();
System.out.println("按值从头删除:"+(endTime_removebyvalue_allfromhead-startTime_removebyvalue_allfromhead)+"ns");}
}
运行结果:
插入到头1~1000时间:624400ns
1
2
...
1000
按值从头删除:40511100ns
去掉SLList的按值删除的打印命令后:
插入到头1~1000时间:435100ns
按值从头删除:2881500ns
删除成功!
以上是关于Java的int -128到127的问题与解决办法的主要内容,如果未能解决你的问题,请参考以下文章