首页 > 代码库 > java集合(一)
java集合(一)
1、关于hashCode和equals的处理
1.1为什么在重写了equals()方法之后也必须重写hashCode()方法
在java中,所有的对象都是继承于object类,object类有两个方法equals,hashCode.
1)在没有重写equals方法,我们的类是继承了object的equals方法,object比较的 是两个对象的内存地址,如果new了两个对象内存地址肯定不一样:
对于值对象,== 比较的是两个对象的值
对于引用对象,比较的是两个对象的地址
2)hashcode主要为散列表
如果一个对象的equals没有被改写,那么在执行时无论调用多少次hashCode()总是 返回相同的值(对象的内部地址转化为该数值)。
如果两个对象相等(equals相等),那么对应的hashCode也相等。
两个对象不相等(equals不相等),其对应的hashCode也不相等,这种规范不是必须的(亦即两个对象不相等,其对应的hashCode可能相等)。延伸理解,hashCode相等的两个对象不一定相等(equals相等)。但是建议遵守为两个不同对象返回不同的散列码的规范(比如重写时,但是Object.hashCode()确实为不同的对象返回不同的hashCode), 这种可以提升hash tables的性能。
2.一般如果使用java中的map对象进行存储的时候,他会自动调用hashcode方法来比较两个对象是否相等。
1)重写equals方法时需要重写hashCode方法,主要是针对Map、Set等集合类型的使用;
a: Map、Set等集合类型存放的对象必须是唯一的;
b: 集合类判断两个对象是否相等,是先判断HashCode返回值是否ture,在判断equals是否返回TRUE,只有两者都返回ture,才认为该两个对象是相等的。
2)、由于Object的hashCode返回的是对象的hash值,所以即使equals返回TRUE,集合也可能判定两个对象不等,所以必须重写hashCode方法,以保证当equals返回TRUE时,hashCode也返回Ture,这样才能使得集合中存放的对象唯一。
2.ArrayList的subList结果不可强制转换成ArrayList.
//ArrayList数组表的subList()方法截取的是同内存一个片段,
//在截取后返回新的ArrayList数组表增加、删减、排序,都会对原数组表产生影响!
//下面是源码
// SubList(AbstractList<E> parent,
// int offset, int fromIndex, int toIndex) {
// this.parent = parent;
// this.parentOffset = fromIndex;
// this.offset = offset + fromIndex;
// this.size = toIndex - fromIndex;
// this.modCount = ArrayList.this.modCount;
// }
// public void add(int index, E e) {
// rangeCheckForAdd(index);
// checkForComodification();
// parent.add(parentOffset + index, e);
// this.modCount = parent.modCount;
// this.size++;
// }
//
// public E remove(int index) {
// rangeCheck(index);
// checkForComodification();
// E result = parent.remove(parentOffset + index);
// this.modCount = parent.modCount;
// this.size--;
// return result;
// }
public static void main(String[] args){
List<String> list = new ArrayList<String>();
list.add("fei");
list.add("long");
list.add("feiong");
list.add("飞天奔月");
List<String> listSub = list.subList(1, 3);
System.out.println("listSub"+listSub);//listSub[long, feiong]
System.out.println("list :"+list);//list :[fei, long, feiong, 飞天奔月]
listSub.add("hhhaahha");
System.out.println("listSub"+listSub);//listSub[long, feiong, hhhaahha]
System.out.println("list :"+list);//list :[fei, long, feiong, hhhaahha, 飞天奔 月]
//listSub的第一个删除了
//list中的同样的元素也删除了
listSub.remove(0);
System.out.println("listSub"+listSub);//listSub[feiong, hhhaahha]
System.out.println("list :"+list);//list :[fei, feiong, hhhaahha, 飞天奔月]
//父列表不能删除SubList中的首个元素了--因为offset不能更改
//list.remove(0);
list.remove(3);
System.out.println("listSub"+listSub);//报出异常
}
3.使用集合转数组时,必须集合的toArray(T[] array) 传入的是类型完全一样的数组大小
@Test
public void test(){
Long[] a = {1L, 2L, 3L, 4L};
List<Long> list = new ArrayList<Long>();
list.add(4L);
list.add(5L);
System.out.println("a" + a);//a[Ljava.lang.Long;@4ee00c09
//当入参的a.size > list.length,在a上面修改,并且在a[list.size] == null
a = list.toArray(a);
System.out.println("a" + a);//a[Ljava.lang.Long;@4ee00c09
Long[] b = {1L};
//当入参的a.size < list.length,返回值是新建的一个数组
list.toArray(b);
System.out.println("b" + b);//b[Ljava.lang.Long;@68111f9b
b=list.toArray(b);
System.out.println("b" + b);//b [Ljava.lang.Long;@3c322e7d
}
4.使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法 (根据源码得知asList返回的是内部定义的ArrayList,它并没有实现增删改查方法)
@SafeVarargs
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
/**
* @serial include
*/
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {
if (array==null)
throw new NullPointerException();
a = array;
}
public int size() {
return a.length;
}
public Object[] toArray() {
return a.clone();
}
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
return Arrays.copyOf(this.a, size,
(Class<? extends T[]>) a.getClass());
System.arraycopy(this.a, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
public E get(int index) {
return a[index];
}
public E set(int index, E element) {
E oldValue = http://www.mamicode.com/a[index];
a[index] = element;
return oldValue;
}
public int indexOf(Object o) {
if (o==null) {
for (int i=0; i<a.length; i++)
if (a[i]==null)
return i;
} else {
for (int i=0; i<a.length; i++)
if (o.equals(a[i]))
return i;
}
return -1;
}
public boolean contains(Object o) {
return indexOf(o) != -1;
}
}
5.java 集合中泛型通配符 用了之后就不能添加 元素了 为什么?
//通配符只能出现在引用的定义中,而不能出现在创建对象中。例如:new ArrayList<?>(),这是不可以的。ArrayList<?> list = null,这是可以的。
public static void fun(List<?> list){
//list.add(list.get(0)); 编译不通过
System.out.println("已经打印了"+list);
}
//1.通配符的上届: Anilmal,Cat,dog
//
// public static void act(List<? extends Anilmal> list) {
// for (Anilmal anilmal : list) {
// animal.eat();
// list.add(new Animal("animal")); //编译不通过
// list.add(new Bird("bird"));//编译不通过
// list.add(new Cat("cat"));//编译不通过
// }
//“? extends Animal”则表示通配符“?”的上界为Animal,换句话说就是,“? extends Animal”可以代表Animal或其子类,可代表不了Animal的父类(如Object),因为通配符的上界是Animal
//三个“add”操作都可能引发类型不兼容问题,而传入的参数是未知的,所以java为了保护其类型一致,禁止向List<? extends Animal>添加任意对象,不过却可以添加null,即list.add(null)是可行的
//2.通配符的下届
//既然有了通配符的上界,自然有着通配符的下界。可以如此定义通配符的下界 List<? super Bird>,其中”Bird“就是通配符的下界
//为了保护类型的一致性,因为“? super Bird”可以是Animal,也可以是Object或其他Bird的父类,//因无法确定其类型,也就不能往List<? super Bird>添加Bird的任意父类对象。
// 既然法无确定其父类对象,那该如何遍历List<? super Bird> ? 因为Object是所有类的根类,所//以可以用Object来遍历
6.for循环删除(ArrayList.remove)
for循环的时候是不允许删除list对象的。
因为ArrayList的父类AbstractList里有个modCount的字段记录着List的总数,for循环的时候如果增加或者删除了元素,(修改不会影响),此字段会变化,那么在下次for循环的时候检查到跟之前的长度不同,此时会报ConcurrentModificationException异常。
解决办法是一般用Iterator遍历
java集合(一)