Java的int -128到127的问题与解决办法

Posted 从零开始的智障生活

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java的int -128到127的问题与解决办法相关的知识,希望对你有一定的参考价值。

目录

一、问题描述

二、问题解析

2.1 类型解析

2.2 包装局限

2.3 解决办法


一、问题描述

写了个实现单链表的代码,其中有一条按值删除,测试单链表插入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是基本数据类型,有两种方式,可以使两者相等:

  1. int类型转换成int的包装类java.lang.Integer。在赋值之后it会被修改为Integer类型;
    it==(Object)i
  2. 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。

  1. 两个分别定义的相同基本数据类型,如 int,如果值相等,那么==的话肯定是true;
  2. 两个分别定义的对象类型类型,如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时。

  1. 当int为基本数据类型时,值相等,可以一直相等;
  2. 当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);
    */
  3. 当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()方法。可以总结为:

  1. 如果用的数据类型,没有自定义的equals()方法,那么如果判断相等,就相当于==,即判断地址相等;
  2. 如果用的数据类型,有自定义的【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的问题与解决办法的主要内容,如果未能解决你的问题,请参考以下文章

java中如何把string类型转换成int类型

java里如何把String字符串转换成int[]数据?

java里,如何把String字符串转换成int[]数组?

java里,如何把String字符串转换成int[]数组?

java中int类型怎么转biginteger类型

java Java String转换为Int