Collection中的list查找
集合
最近在学习集合相关的东西,今天在玩list的时候发现了两个奇怪的情况,先上第一个情况
IndexOutOfBoundsException
代码如下:
public class TestCollection {
public static void main(String[] args) {
List l1 = new LinkedList();
l1.add(0,"3");
l1.add(1,"321");
l1.add(2,"3213");
l1.add(5,"3261");
l1.add(3,"3241");
l1.add(4,"3221");
System.out.println(l1);
}
}
控制台输出
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 5, Size: 3
at java.util.LinkedList.checkPositionIndex(LinkedList.java:560)
at java.util.LinkedList.add(LinkedList.java:507)
at Test.TestCollection.main(TestCollection.java:14)
Process finished with exit code 1
先看下错误说明是指下标超出界限,按照我自己之前的理解是,list的add方法,会按照顺序插入相应的元素,但是实际情况好像并不是这样的,因为看5的那个地方发现并没有按照我想象的那样去插入数据,原因是什么呢?我就去看了一下源码----------
/**
* Inserts the specified element at the specified position in this list.
* Shifts the element currently at that position (if any) and any
* subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
上面这一段是插入方法的介绍,首先是一个检查下标的方法,如果下标的数值和list大小数值一样,那么就把它插入到最后一个位置,否则依次插入。
下面详细看一下其中的方法:
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
上面这个方法是检查下标的方法,就是第一个先检查下标,一下子就看到了问题所在,和控制台看到的错误一毛一样!!!
根据我的理解是,list的size大小是根据插入的时候扩充的,如果一下子输入超出size的下标,就会产生Exception in thread “main” java.lang.IndexOutOfBoundsException: Index: 5, Size: 3
这样的报错(其中index是下标,size是数组的大小),下标超过了list的大小。
下面是添加元素的方法,感兴趣的可以看一下,不过相信也都实现过了:
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
/**
* Inserts element e before non-null Node succ.
*/
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
感觉自己对集合的理解还是不够,所以产生了这样的错误,根据我的理解,如果事先设定好list的大小,就能避免这个问题。
下标查找错误
感觉这个问题就非常有意思了
先看一下代码:
public class TestCollection {
public static void main(String[] args) {
List l1 = new LinkedList();
l1.add(0,"3");
l1.add(1,"321");
l1.add(2,"3213");
l1.add(3,"3241");
l1.add(4,"3221");//为什么这个位置的查找失败,根据index查找
l1.add(5,"3261");
l1.remove(1);
System.out.println(Collections.binarySearch(l1,"3241"));
System.out.println(Collections.binarySearch(l1,"3221"));
System.out.println(Collections.binarySearch(l1,"3261"));
System.out.println(l1);
}
}
再看一下控制台输出
2
-3
4
[3, 3213, 3241, 3221, 3261]
Process finished with exit code 0
移除一个元素之后,发现除了***l1.add(4,“3221”)***前后输出的下标位置都正确,只有这个不正确,而且有点离谱,先去看一下源码逻辑
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
判断集合是否是RandomAccess的实例,并且sizi小于5000;
如果是,那就按照按index(小标)查找,如果不是那就iterator(迭代查找),我们是第一个,跟进去
private static <T>
int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {
int low = 0;
int high = list.size()-1;
while (low <= high) {
int mid = (low + high) >>> 1;
Comparable<? super T> midVal = list.get(mid);
int cmp = midVal.compareTo(key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
上面这些代码就是具体的查找逻辑,先定义一个高低(虽然并不知道为什么这么定义,感觉max和min更合适一点)其中初始值low为0,high为size的大小,本程序high为5,然后运算取mid,然后比较获取到的值midVal和key,这个k就是我们list的第二个值,然后返回取cmp,然后运算的到cmp为-3,得到这个结果,解释说list的查找,不但与index有关,与后面的key也有关系,key也应该按顺序排列,这是我的理解,但是总感觉不对,这个样子的代码岂不是存在这样的一个bug,或者也可能是我对集合的了解还是不够深入吧。