概述
功能:该类提供了List
相关类/接口的基础框架
开始版本:JDK 1.2
注意:
1. 本类为抽象
类
2. 可以存在相同的值,也可以为null
3. 数据结构:顺序表、链表
继承类:AbstractCollection<E>
实现接口:List<E>
所在包:java.util
导入类:无
类声明:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
备注:本方法没有单独重写toString()
方法,使用的是AbstractCollection
的toString()
方法
框架图:
属性
变量
保护变量
01.当前对象被修改的次数modCount
// 修改次数:初始为0
protected transient int modCount = 0;
方法
构造器
// 唯一构造器
protected AbstractList() {}
内部类
01.迭代器 Itr
注意:
1. 通过迭代器中的remove()
方法,对象的变量modCount
和迭代器内部的expectedModCount
均会改变,同步了修改,因此不会抛出ConcurrentModificationException
异常
2. 使用本类的移除
有关方法(非迭代器方法),只会改变对象的变量modCount
,不会改变内部变量expectedModCount
,导致对象和迭代器未同步,会导致ConcurrentModificationException
异常
3. lastRet
变量为当前元素的前一个元素下标,迭代器的remove()
方法移除的是前一个元素(即上一次调用next()
获取的元素),删除后后面的元素全部前移了一位,因此需要将当前元素的游标会前移一位,否则就跳过了原先此下标的元素
4. 迭代器的remove()
方法移除前一个元素,这样不会破坏原对象遍历结果,而删除后面元素会影响遍历结果,因此若遍历中进行了增删改,遍历出来的结果和实际对象中的元素并不一定一致
5. 迭代器的remove()
不能连续调用两次,如果不禁止连续删除而是直接给lastRet
和cursor
减一原则上可以(开始仍然判断lastRet < 0
),但会将已经遍历的元素删除容易使数据混乱,因此删除一次后会直接将lastRet
会置为-1
,而remove()
方法开始就校验了lastRet
不能小于0
// 源码
private class Itr implements Iterator<E> {
// 游标后一个元素的下标
int cursor = 0;
// 上一个使用next()获取的元素下标,若元素被删除,此值重置为-1
int lastRet = -1;
// 当前集合被修改的次数
int expectedModCount = modCount;
// 判断游标后是否还有元素
public boolean hasNext() {
return cursor != size();
}
// 返回当前游标后的元素,实际调用的是get()方法
public E next() {
// 校验是否生成迭代器后有修改过值,若有则抛出ConcurrentModificationException异常
checkForComodification();
try {
int i = cursor;
E next = get(i);
// 移动游标
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
// 移除元素
public void remove() {
// 比较了当前游标是否在第一个元素之前或刚进行过移除操作,是则直接报错(remove()方法移除的是当前游标前的元素)
if (lastRet < 0)
throw new IllegalStateException();
// 校验修改次数是否与生成迭代器一致,不一致抛出ConcurrentModificationException异常
checkForComodification();
try {
// 调用集合的移除元素方法,移除的是当前游标前的元素
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
// 移除元素后此值设置为-1,只要调用了next()移动了游标此处就不再是-1,而连续删除不会修改lastRet,仍是-1
lastRet = -1;
// 修改集合的修改次数并同步至迭代器(就是这里使得通过迭代器移除元素不会报错)
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
// 校验修改次数是否与生成迭代器一致,不一致则抛出ConcurrentModificationException异常
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
// 错误示例1:游标未移动就删除元素
@Test
void contextLoads3() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
list.add("7");
list.add("8");
list.add("9");
list.add("10");
Iterator<String> it = list.iterator();
// 还未进行游标移动,游标前无元素,抛出java.lang.IllegalStateException异常
it.remove();
}
// 错误示例2:连续调用迭代器移除方法
@Test
void contextLoads() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
list.add("7");
list.add("8");
list.add("9");
list.add("10");
Iterator<String> it = list.iterator();
it.next();
it.next();
it.next();
it.next();
// 此处已设置lastRet为-1
it.remove();
// lastRet为-1,抛出java.lang.IllegalStateException异常java.lang.IllegalStateException
it.remove();
}
// 错误示例3:生成迭代器后调用对象的删除方法(增加方法一样)
@Test
void contextLoads() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
list.add("6");
list.add("7");
list.add("8");
list.add("9");
list.add("10");
Iterator<String> it = list.iterator();
it.next();
list.remove(0);
// 抛出ConcurrentModificationException异常
it.next();
}
02.List迭代器 ListItr
注意:
1. 本迭代器继承于Iterator
,单独为List
集合提供了一些迭代器方法
2. 本迭代器并非游标固定在两端,而是将游标初始化在传输的集合下标前,传输0则从将游标放于开头
3. 除却迭代器通有的hasNext()
、next()
、remove()
及一些校验方法,增加了适用于集合的hasPrevious
、previous()
、nextIndex()
、previousIndex()
、set()
、add()
方法
4. 集合原有的add()
方法会使得modCount++
,而迭代器增加的add()
同步了修改次数,set()
两者均未对modCount
变量操作,效果一致,但若有些集合实现类对set()
进行了修改使得modCount
改变仍会对迭代器有影响,因此建议优先使用迭代器内的修改元素方法
5. set()
和remove()
方法均在开始时校验了lastRet
变量不为-1,因此这两个操作中间必须进行next()
操作,原因仍是连续操作会对已经遍历的元素产生影响容易使数据混乱,而add()
方法不会对已遍历过的元素产生影响则未判断
// 源码
private class ListItr extends Itr implements ListIterator<E> {
// 构造器,根据指定下标放置游标
ListItr(int index) {
cursor = index;
}
// 游标之前是否还有元素,当前游标位置为0则表示之前没有元素
public boolean hasPrevious() {
return cursor != 0;
}
// 获取前一个元素
public E previous() {
checkForComodification();
try {
int i = cursor - 1;
E previous = get(i);
lastRet = cursor = i;
return previous;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
// 获取游标后一个元素下标,即 cursor
public int nextIndex() {
return cursor;
}
// 获取游标前一个元素下标,即 cursor - 1
public int previousIndex() {
return cursor-1;
}
// 修改元素方法,同原迭代器的remove不同,此方法未改变lastRet变量,因此可以连续修改元素,只是修改的为同一个元素
// 本方法虽然进行了修改次数同步,到实际有没有增加此处,与集合实现类的set()方法有关
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.set(lastRet, e);
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
// 本方法与原迭代器的remove一样,操作后变更了lastRet为-1,因此不能连续进行add操作,
// 本方法同步了修改次数,不会直接导致ConcurrentModificationException异常
public void add(E e) {
// 校验是否在生成迭代器后有进行集合的修改操作(一般为add操作)
checkForComodification();
try {
int i = cursor;
AbstractList.this.add(i, e);
lastRet = -1;
cursor = i + 1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
// 错误示例
@Test
void contextLoads() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
ListIterator<String> iterator = list.listIterator();
iterator.next();
iterator.remove();
iterator.add("11");
// 连续调用了remove()和set()方法,虽然中间存在一个add()方法,但未调用next(),抛出了java.lang.IllegalStateException异常
iterator.set("0");
}
03.提供给截取元素的集合(未实现RandomAccess) SubList
注意:
1. 本集合内部类继承AbstractList
,因此本类的实现类调用subList()
返回的集合对象不能转换为具体实现类的类型
2. 本方法中途也不允许对集合使用非本类的add()
、remove()
方法,不建议使用非本类的set()
方法
3. 本方法重写了get()
方法,虽然看似是截取了原集合,但其实只是修改了获取方法,虽然本方法内的add()
、remove()
、set()
、get()
等方法的下标针对的是截取后的集合,但是对象的指向还是原集合对象,因此增删改也会影响到原集合对象
4. 本方法的修改、移除等操作底层实际调用的是AbstractList
的方法,本类的方法仅仅是进行了一些其他的处理,具体实现可以查看AbstractList
对应方法详细解析
// 源码
class SubList<E> extends AbstractList<E> {
// 储存传入需截取的集合
private final AbstractList<E> l;
// 储存起始截取下标
private final int offset;
// 储存需截取的元素数量(终止-起始)
private int size;
// 构造器,需传入需截取的集合对象,截取起始位置,截取终止位置
SubList(AbstractList<E> list, int fromIndex, int toIndex) {
// 校验下标,起始不能小于0,终止不能大于集合长度,起始不能大于终止
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > list.size())
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
l = list;
offset = fromIndex;
size = toIndex - fromIndex;
this.modCount = l.modCount;
}
// 设置截取后的集合指定下标的元素
public E set(int index, E element) {
rangeCheck(index);
checkForComodification();
// 此处下标需增加起始下标位置,因为集合未去除之前和之后的元素
return l.set(index+offset, element);
}
// 获取截取后的集合指定下标的元素
public E get(int index) {
rangeCheck(index);
checkForComodification();
// 此处下标需增加起始下标位置,因为集合未去除之前和之后的元素
return l.get(index+offset);
}
// 获取截取后集合的元素个数
public int size() {
checkForComodification();
return size;
}
// 给截取后的集合指定下标新增元素
public void add(int index, E element) {
rangeCheckForAdd(index);
checkForComodification();
// 此处下标需增加起始下标位置,因为集合未去除之前和之后的元素
l.add(index+offset, element);
this.modCount = l.modCount;
// 增加截取后集合的元素个数
size++;
}
// 移除截取后的集合指定下标的元素
public E remove(int index) {
rangeCheck(index);
checkForComodification();
E result = l.remove(index+offset);
this.modCount = l.modCount;
size--;
return result;
}
// 移除截取后的集合指定范围的元素
protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
l.removeRange(fromIndex+offset, toIndex+offset);
this.modCount = l.modCount;
// 减少截取后集合的元素个数
size -= (toIndex-fromIndex);
}
// 给截取后的集合新增指定集合内的全部元素
// 本方法调用的addAll(int index, Collection<? extends E> c)方法
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
// 给截取后的集合以指定下标为起始,新增指定集合内的全部元素
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
int cSize = c.size();
if (cSize==0)
return false;
checkForComodification();
l.addAll(offset+index, c);
this.modCount = l.modCount;
size += cSize;
return true;
}
// 给截取后的集合生成List迭代器,实际生成的是AbstractList的List迭代器
public Iterator<E> iterator() {
return listIterator();
}
// 给截取后的集合生成以指定下标前为游标起始位置的List迭代器,实际生成的是AbstractList的List迭代器
public ListIterator<E> listIterator(final int index) {
checkForComodification();
rangeCheckForAdd(index);
return new ListIterator<E>() {
// 此处下标需增加起始下标位置,因为集合未去除之前和之后的元素
private final ListIterator<E> i = l.listIterator(index+offset);
// 游标后是否有元素
public boolean hasNext() {
return nextIndex() < size;
}
// 获取游标后的元素,若游标后没有元素抛出NoSuchElementException异常
public E next() {
if (hasNext())
return i.next();
else
throw new NoSuchElementException();
}
// 游标前是否有元素
public boolean hasPrevious() {
return previousIndex() >= 0;
}
// 获取游标前的元素,若游标前没有元素抛出NoSuchElementException异常
public E previous() {
if (hasPrevious())
return i.previous();
else
throw new NoSuchElementException();
}
// 获取游标后一个元素的下标,需要减去起始元素下标,因为集合未去除之前和之后的元素
public int nextIndex() {
return i.nextIndex() - offset;
}
// 获取游标前一个元素的下标,需要减去起始元素下标,因为集合未去除之前和之后的元素
public int previousIndex() {
return i.previousIndex() - offset;
}
// 移除元素,同步了修改次数,调用的方法有校验lastRet,不能连续调用
public void remove() {
i.remove();
SubList.this.modCount = l.modCount;
// 减少截取后集合的元素个数
size--;
}
// 修改元素
public void set(E e) {
i.set(e);
}
// 新增元素,同步了修改次数,调用的方法有校验lastRet,不能连续调用
public void add(E e) {
i.add(e);
SubList.this.modCount = l.modCount;
// 增加截取后集合的元素个数
size++;
}
};
}
// 截取当前集合指定范围的元素
public List<E> subList(int fromIndex, int toIndex) {
return new SubList<>(this, fromIndex, toIndex);
}
// 校验集合下标,下标可以等于集合个数,用于移除、修改元素,最后一位下标是size-1,size位置没有元素
private void rangeCheck(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 校验集合下标,下标不能等于集合个数,用于新增相关校验,最后一位下标是size-1,可以在size位置新增元素
private void rangeCheckForAdd(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 校验集合下标异常信息
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
// 校验迭代器生成后集合是否提供非迭代器方法修改元素(一般为新增和移除)
private void checkForComodification() {
if (this.modCount != l.modCount)
throw new ConcurrentModificationException();
}
}
04.提供给截取元素的集合(实现RandomAccess) RandomAccessSubList
注意:
1. 本类继承了SubList
,实际上还是调用了未实现RandomAccess
接口时调用的内部类生成集合
2. 本类为List
的底层类,未进行RandomAccess
实现的特别区分,仅留下了框架,实现类若需不同处理需要重写此内部类
拓展:RandomAccess
是一个标志接口,对于一些方法可以通过此标识判断其是否支持快速随机访问
// 源码
class RandomAccessSubList<E> extends SubList<E> implements RandomAccess {
// 继承了SubList,调用的是SubList的构造器
RandomAccessSubList(AbstractList<E> list, int fromIndex, int toIndex) {
super(list, fromIndex, toIndex);
}
// 继承了SubList,调用的是SubList的构造器
public List<E> subList(int fromIndex, int toIndex) {
return new RandomAccessSubList<>(this, fromIndex, toIndex);
}
}
保护方法
01.移除指定下标范围的元素 removeRange(int fromIndex, int toIndex)
参数:
1. fromIndex
—— 起始下标
2. toIndex
—— 终止下标
// 源码
protected void removeRange(int fromIndex, int toIndex) {
// 创建一个游标开始位置为指定开始下标之前的迭代器
ListIterator<E> it = listIterator(fromIndex);
// 循环开始下标至结束下标,一个一个元素移除
for (int i=0, n=toIndex-fromIndex; i<n; i++) {
it.next();
it.remove();
}
}
抽象方法
01.获取指定下标的元素 get(int index)
// 源码
abstract public E get(int index);
私有方法
01.校验下标是否超过集合下标范围 rangeCheckForAdd(int index)
参数:index
—— 需要校验的下标
备注:超过集合下标范围有两种情况 —— 小于0和大于集合长度
// 源码
private void rangeCheckForAdd(int index) {
if (index < 0 || index > size())
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
02.超出下标异常信息 outOfBoundsMsg(int index)
参数:index
—— 校验出现异常信息的只当下标
备注:信息格式 —— Index: 指定下标, Size: 集合长度
// 源码
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size();
}
// 示例
@Test
void contextLoads() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
// Index: 7, Size: 5
list.add(7,"10");
}
公有方法
01.新增元素 add(E e)
参数:e
—— 增加的元素
注意:
1. 此新增元素方法调用的是add(int index, E element)
,指定的下标为集合长度(下标从0开始,长度从1开始),长度即最后一个元素后一位的下标
2. 本方法调用的add(int index, E element)
是必须重写,未重写会抛出UnsupportedOperationException
异常
// 源码
public boolean add(E e) {
add(size(), e);
return true;
}
02.新增元素到指定下标 add(int index, E element)
参数:
1. index
—— 新增元素的指定下标
2. element
—— 新增的元素
注意:本方法必须重写,未重写会抛出UnsupportedOperationException
异常
// 源码
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
03.设置指定下标的元素为指定元素 set(int index, E element)
参数:
1. index
—— 修改的元素下标
2. element
—— 修改为的元素
注意:本方法必须重写,未重写会抛出UnsupportedOperationException
异常
// 源码
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
04.移除指定下标的元素 remove(int index)
参数: index
—— 移除元素的下标
注意:本方法必须重写,未重写会抛出UnsupportedOperationException
异常
// 源码
public E remove(int index) {
throw new UnsupportedOperationException();
}
05.获取指定元素第一次出现下标 indexOf(Object o)
参数: o
—— 需获取的元素
注意:
1. 获取的是集合中指定元素第一次出现的下标
2. 返回-1
表示没有找到指定元素
补充:previousIndex()
—— 返回游标前一个元素的下标,游标开始时在第一个元素前,调用next()
后游标后移
// 源码
public int indexOf(Object o) {
ListIterator<E> it = listIterator();
if (o==null) {
// 如果指定元素为null需要单独比较,直接使用equals会空指针
while (it.hasNext())
if (it.next()==null)
return it.previousIndex();
} else {
while (it.hasNext())
// 此处已调用了next(),游标已后移,因此返回的下标为游标的前一个元素下标
if (o.equals(it.next()))
return it.previousIndex();
}
return -1;
}
06.获取指定元素最后一次出现的下标 lastIndexOf(Object o)
参数: o
—— 需获取的元素
补充:
1. listIterator(size())
—— 创建一个开始游标在最后一个元素之后的迭代器
2. it.hasPrevious()
—— 判断当前游标前面有没有元素
3. it.nextIndex()
—— 获取当前游标后一位的元素下标 (此处是逆序的迭代器,因此要返回游标后的下标),调用it.previous()
后游标前移
4. 以上这些方法在本类中有实现,后续有说明
5. 该方法获取的是最后一次出现下标,因此创建迭代器的游标在最后,反向遍历
6. 该方法未找到指定元素返回-1
// 源码
public int lastIndexOf(Object o) {
ListIterator<E> it = listIterator(size());
if (o==null) {
// 如果指定元素为null需要单独比较,直接使用equals会空指针
while (it.hasPrevious())
if (it.previous()==null)
return it.nextIndex();
} else {
while (it.hasPrevious())
// 此处已调用了previous(),游标已后移,因此返回的下标为游标的前一个元素下标
if (o.equals(it.previous()))
return it.nextIndex();
}
return -1;
}
07.清空集合 clear()
注意:
1. 本方法调用移除指定下标范围的元素,开始下标为0,结束下标为集合元素个数
2. 本方法实际上是一个一个元素移除的
// 源码
public void clear() {
removeRange(0, size());
}
08.在指定下标新增指定集合内全部元素 addAll(int index, Collection<? extends E> c)
参数:
1. index
—— 新增集合元素的起始下标
2. c
—— 新增的集合
注意:
1. 此方法实际调用的是add(int index, E element)
,此方法必须重写,否则会抛出UnsupportedOperationException
异常
2. 本方法是循环全部指定集合进行多次元素插入实现的,因此重复元素也会插入,不会去重
// 源码
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
boolean modified = false;
for (E e : c) {
add(index++, e);
modified = true;
}
return modified;
}
09.生成当前集合的迭代器 iterator()
注意:此处生成的迭代器为本类中的迭代器内部类
// 源码
public Iterator<E> iterator() {
return new Itr();
}
10.生成当前集合的List
迭代器 listIterator()
注意:
1. 此处生成的迭代器为本类中的List
迭代器内部类
2. 指定游标位置设置在集合开头便于遍历全部元素
// 源码
public ListIterator<E> listIterator() {
return listIterator(0);
}
11.生成当前集合指定下标前为起始位的List
迭代器 listIterator(final int index)
注意:此处生成的迭代器为本类中的List
迭代器内部类
// 源码
public ListIterator<E> listIterator(final int index) {
// 校验指定下标是否小于0或超过集合长度
rangeCheckForAdd(index);
return new ListItr(index);
}
12.截取集合指定范围内的元素 subList(int fromIndex, int toIndex)
参数:
1. fromIndex
—— 起始下标
2. toIndex
—— 终止下标
注意:本方法返回的集合虽然看似是截取了原集合的一部分,但其实只是修改了get()
等方法,返回的对象仍然是原集合对象,因此对返回的集合进行修改仍会变更原集合
补充:
1. RandomAccess
是一个标志型的接口,内部无内容也无注释
2. 根据本类是否实现了RandomAccess
接口匹配不同的处理,主要用于不同的实现类使用效率更高的处理方法
3. 本类为List的底层类,未进行RandomAccess
实现的特别区分,因此本类无论是否实现了RandomAccess
,均返回的是SubList
内部类,但此类的实现类ArrayList
实现了此接口,而LinkedList
则没有实现此接口
// 源码
public List<E> subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ?
new RandomAccessSubList<>(this, fromIndex, toIndex) :
new SubList<>(this, fromIndex, toIndex));
}
// 示例
@Test
void contextLoads5() {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
List<String> list1 = list.subList(1, 3);
// [2, 3]
System.out.println("截取后的集合" + list1);
// [1, 2, 3, 4, 5]
System.out.println("原集合:" + list.toString());
list1.set(0,"11");
// [11, 3]
System.out.println("截取后的集合" + list1);
// [1, 11, 3, 4, 5]
System.out.println("原集合:" + list.toString());
list1.add("22");
// [11, 3, 22]
System.out.println("截取后的集合" + list1);
// [1, 11, 3, 22, 4, 5]
System.out.println("原集合:" + list.toString());
list1.remove(2);
// [11, 3]
System.out.println("截取后的集合" + list1);
// [1, 11, 3, 4, 5]
System.out.println("原集合:" + list.toString());
}
13.比较方法 equals(Object o)
参数:o
—— 比较的对象
// 源码
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
14.哈希值 hashCode
计算方法:31 * 当前哈希值 + 元素哈希值(null为0)
,当前哈希值随着循环不断累积,首位为1
// 源码
public int hashCode() {
int hashCode = 1;
for (E e : this)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}