JavaSE基础八----<集合>List接口及其实现类,List接口的迭代
Posted 小智RE0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaSE基础八----<集合>List接口及其实现类,List接口的迭代相关的知识,希望对你有一定的参考价值。
目录
List接口及其实现类
List 接口继承了 Collection 接口以定义一个允许重复项的有序集合。
特点:可以存储重复数据,且为有序的(有序即按照元素的存储顺序进行排序).
List接口有三个实现类;
ArrayList,LinkedList,Vector.
List中继承了Collection的一些方法之外,自己也定义了一些方法;具体的方法将在List接口的实现类中进行学习.
1List接口的实现类(图解认识)
1.1ArrayList的认识(图解)
ArrayList底层为数组实现,查询快,增删慢
ArrayList实现了长度可变的数组,在内存中分配连续的空间;遍历元素和随机访问元素的效率比较高.
在第一次添加元素时;创建数组,会在底层创建默认长度为10的数组(自己可以更改);当数据内容满了之后,继续添加元素时,数组会自动地扩容一段原来长度的1.5倍的新数组.
效果图:
缺点:扩容后,如果元素不能存满时,空间浪费.
在增添元素时,如果是中间插入,那么后面的元素一个一个往后移动;在删除数组中的某个元素时,后面的元素依次往前移动,所以增删慢
.
1.2LinkedList 的认识(图解)
LinkedList实现类,底层使用链表存储数据;LinkedList是以双链表存储的
查询慢,增删快.
效果图:
在一个节点中,数据存储在数据域中,头指针和尾指针;负责指向上一个节点和下一个节点的地址链接工作.
那么,在元素查找时,从第一个节点开始向后查找,所以查找慢;
增加,删除元素时,只需要改变头指针尾指针的指向位置即可;其他元素不移动;增删快.
Vector实现类:
底层也是数组实现,默认容量为10;扩容速度为原来的2倍;添加了同步锁,是线程安全的.
2.List接口的实现类具体内容
在创建实现类对象时,可以使用多态的方式,即List接口的引用变量指向实现类的对象,但是这时,创建的变量只能调出List的方法以及List以上(例如Collection接口)的方法;而不能调出实现类里面的自己的方法;所以一般用子类的引用变量指向子类的创建对象.
2.1Arraylist
2.1(1)ArrayList的构造方法
默认调用无参的构造方法,创建ArrayList对象时,并没有创建数组;即空数组.
- public ArrayList( )
//无参构造方法
ArrayList<String> a=new ArrayList<>();
//进入到ArrayList底层源码
//在没有添加任何元素时,这时实际上数组还没有创建;当填入数据元素时,默认创建长度为10的数组;
/*public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}*/
//transient Object[] elementData; // non-private to simplify nested class access
有参构造方法
- public ArrayList(Collection<? extends E> c)
将另一个集合直接传入这个集合. - public ArrayList(int initialCapacity)
创建指定长度的数组;
ArrayList<String> a=new ArrayList<>(3);//创建长度为3的数组
查看这个创建指定长度数组,构造方法源码;判断传入的参数,如果为0,仍然不创建数组,如果为负数,有抛出异常提示处理;
(注意:这个传入的参数有最大值限制,为整数的最大值减去8)
源码:private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
2.1(2)ArrayList中的常用方法
添加方法
- 1.public boolean add(E e);
add方法,默认向尾部添加元素;返回值为布尔值;
//例如,给定长度为5,一开始底层创建长度为5的数组;
ArrayList<String> a=new ArrayList<>(5);
//public boolean add(E e);add方法,默认向尾部添加元素;
a.add("a");a.add("b");a.add("c");a.add("d");a.add("e");
//这里先进行检测,通过grow()方法进行扩容;扩容一个长度为7的新数组;
a.add("f");a.add("g");
//这里先进行检测,通过grow()方法进行扩容;扩容一个长度为10的新数组;
a.add("h");a.add("i");a.add("j");
//这里先进行检测,通过grow()方法进行扩容;扩容一个长度为15的新数组;
a.add("k");a.add("l");
//虽然没有填充满元素,但是底层创建的长度为15的数组剩下的3个空间仍然存在
System.out.println(a);
//在这里输出的时候,看到的是size()方法统计的实际传入的元素个数;
//[a, b, c, d, e, f, g, h, i, j, k, l]
实际输出的数组长度为size( )统计的个数;
当数组中不足以存储元素时,调用grow方法进行扩容,新建一个原来数组的长度1.5倍的新数组;注意在grow方法中还用了Arrays中的copyOf方法进行原来数组的复制以及设定新数组长度.
- 2.public void add(int index,E element)
向指定的下标位置插入元素
ArrayList<String> a=new ArrayList<>();
a.add("0");a.add("q");a.add("b");a.add("d");
//向下标索引2位置插入x,这时原本位置的b和d向后移动
//public void add(int index,E element);
a.add(2,"x");
System.out.println(a);//[0, q, x, b, d]
删除方法
- 1.public void clear()
清空所有元素 - 2.public boolean remove(Object o);
从前向后,删除第一个出现的指定元素; - public E remove(int index)
删除指定下标位置的元素,返回值为被删除的元素;
- protected void removeRange(int fromIndex,int toIndex)
受保护权限的方法;在子类中调用;
删除某一段区间[fromIndex:开始下标位置,toIndex:结束下标位置(不包括)]的元素;
import java.util.ArrayList;
//写一个Demo02类继承ArrayList实现类
public class Demo02 extends ArrayList {
public static void main(String[] args) {
//新建Demo02类的对象
Demo02 d2=new Demo02();
d2.add("Q");//0(下标位置)
d2.add("W");//1
d2.add("E");//2
d2.add("R");//3
d2.add("G");//4
//protected void removeRange(int fromIndex,int toIndex)
//受保护权限的方法;在子类中调用;
// 删除某一段区间[fromIndex:开始下标位置,toIndex:结束下标位置(不包括)]的元素;
//删除0`3位置的元素;
d2.removeRange(0,4);
System.out.println(d2);//[G]
}
}
判断方法
和Collection接口的类似
-
public boolean isEmpty();
判断是否为空 -
public boolean contains(Object o)
判断是否包含某个元素
查找方法
- 1.public E get(int index)
查找给定下标位置的元素,返回值为查找的元素;
为什么说它查找比较快;进入get方法源码;可以看到在查找前,首先进行一个检测;传入的下标是否在数组的长度范围内;由于底层是数组结构,可以精确地找到指定下标索引的元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
- 2.public int indexOf(Object o)
从前向后,查找指定元素首次出现的位置;返回值为下标索引数; - 3.public int lastIndexOf(Object o);
从后向前;找指定元素首次出现的位置;返回值为下标索引数;
替换方法
- public E set(int index,E element)
将指定下标位置的元素替换掉;返回为替换掉的元素;
E:被替换的元素;index:下标索引;element:即将换入的元素;
截取方法
- public java.util.List subList(int fromIndex,int toIndex)
截取一段指定位置(formIndex:开始下标位置;toIndex:结束下标位置(不包括))的数组;返回一个新的数组;
//截取方法
//public java.util.List<E> subList(int fromIndex,int toIndex)
//截取一段指定位置(formIndex:开始下标位置;toIndex:结束下标位置(不包括))的数组;返回一个新的数组;
ArrayList<String> a=new ArrayList<>();
a.add("q");//0
a.add("w");//1
a.add("e");//2
a.add("r");//3
a.add("f");//4
a.add("G");//5
System.out.println(a.subList(0,5));//[q, w, e, r, f]
//截取得到的是一个新的数组,原来的数组还在;
System.out.println(a);//[q, w, e, r, f, G]
排序方法
- public void sort(java.util.Comparator<? super E> c)
按指定规则进行排序
在Jdk1.8以后出现的方法
//排序方法;
//public void sort(java.util.Comparator<? super E> c)
ArrayList<String> a=new ArrayList<>();
a.add("5");
a.add("1");
a.add("4");
a.add("2");
System.out.println(a);//未排序之前 [5, 1, 4, 2]
a.sort(new Comparator<String>() {
@Override
//使用匿名内部类;定义排序规则
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
System.out.println(a);//排序后 [1, 2, 4, 5]
遍历方法
- public void forEach(java.util.function.Consumer<? super E> action)
//遍历方法
//public void forEach(java.util.function.Consumer<? super E> action)
//以流的方式遍历,
ArrayList<String> arr=new ArrayList<>();
arr.add("Q");
arr.add("W");
arr.add("A");
//这里的q可用任意字母代替
arr.forEach((q)-> System.out.println(q));
//Q
//W
//A
涉及到两个集合的AII方法
-
1.public boolean addAll(java.util.Collection<? extends E> c)
把指定集合添加到这个集合;返回值为布尔值; -
2.public boolean containsAll(java.util.Collection<?> c)
返回值为布尔值,作用是判断这个集合是否有另一个集合的元素;
只要两个集合中有一个不相同元素;返回false;
只要这个集合完全包含另一个集合的所有元素(另一个集合可以没有这个集合的元素);返回true; -
3.public boolean removeAll(java.util.Collection<?> c)
返回值为布尔值;作用是删除两个集合的相同元素;
如果两个集合中有相同的元素,返回true;
如果两个集合中完全没有相同的元素;返回false; -
4.public boolean retainAll(java.util.Collection<?> c)
返回值为布尔值,作用是保留两个集合中的相同元素;
只要两个集合中有不同的元素,就返回true;
如果两个集合中的元素完全相同;返回false;
2.2 LinkedList
LinkedList的构造方法
两个构造方法;
- public LinkedList( )
无参构造方法,创建一个空链表. - public LinkedList(@NotNull java.util.Collection<? extends E> c)
有参构造方法,传入一个集合.
查看LinkedList的源码;底层是链表结构,每一段数据存到一个Node节点中,item存储数据;由于它的结构不像数组那样紧凑地排放在一块,所以需要prev和next来存储上一个节点和下一个节点的地址.
例如:
LinkedList<String> linkedList1=new LinkedList<>();
linkedList1.add("a");
linkedList1.add("b");
linkedList1.add("c");
linkedList1.add("d");
LinkedList的常用方法
对于它查询慢的原因,可以看看LinkedList的查找方法;
- public E get(int index)
虽然参数为index,但是这并不像数组中的下标排序,这里可理解为一种元素位置的底层序号.
底层有这样一段代码.()类似于数组的折半查找方式 );它在查询数据的时候,要从头节点或者尾节点开始,一个一个去查找,查询效率变低.
Node<E> node(int index) {
// assert isElementIndex(index);
//如果要找的位置序号在左边,从first头结点开始找,
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
//如果要找的位置序号在右边,从last尾节点开始找,
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
返回值 | 方法 |
---|---|
boolean | add(E e) ;将指定的元素加到此列表的末尾。 |
void | add(int index, E element) ;在此列表中的指定位置插入指定的元素。 |
boolean | addAll(Collection<? extends E> c) ;将指定集合中的所有元素加到到此列表的末尾。 |
boolean | addAll(int index, Collection<? extends E> c) ;将指定集合中的所有元素插入到此列表中,从指定的位置开始。 |
void | addFirst(E e) ;把指定的元素e加到列表的头部。 |
void | addLast(E e) 将指定的元素e加到此列表的后面; |
void | clear( ) 删除列表的所有元素。 |
boolean | contains(Object o) 如果此列表包含指定的元素,则返回 true 。 |
E | getFirst() 返回此列表中的第一个元素。 |
E | getLast() 返回此列表中的最后一个元素。 |
int | indexOf(Object o) 返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。 |
int | lastIndexOf(Object o) 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。 |
E | remove( ) ;检索并删除此列表的第一个元素。 |
E | remove(int index) ;删除该列表中指定位置的元素。 |
E | removeFirst( ) ;从此列表中删除并返回第一个元素。 |
E | removeLast( ) ;从此列表中删除并返回最后一个元素。 |
E | peek( ) ;检索但不删除此列表的第一个元素;返回它. |
----- | --------------------------------------------------------------------------------------------------- |
E | pop( ) ;从此列表表示的堆栈中弹出(删除)一个元素。 (出栈);后进先出. |
void | push(E e) 将元素压入到由此列表表示的堆栈上。(入栈) |
----- | --------------------------------------------------------------------------------------------------- |
boolean | offer(E e) 将指定的元素添加为此列表的尾部(最后一个元素)。 (入队)先进先出 |
E | poll() 检索并删除此列表的头(第一个元素)。 (出队) |
3.List接口的实现类迭代
以ArrayList实现类为例;这里写了一个集合为Alist;里面有5个元素,[Q,Q, Q, W, E, R]
通过一个方法进行迭代;
- public java.util.stream.Stream stream();
最终以流的方式输出.
//1.简单的方法遍历;
//public java.util.stream.Stream<E> stream();
Alist.stream().forEach((a)-> System.out.println(a));
for循环遍历
//2.for循环;
for(int i=0;i<Alist.size();i++){
//这里使用get方法获取元素;
System.out.println(Alist.get(i));
}
在遍历的时候,可以对集合中的元素进行删除操作,但是注意下标位置与遍历时的位置不一样了;可能会出错;
//在for循环时想要进行删除元素的操作;
for(int i=0;i<Alist.size();i++){
System.out.println(Alist.get(i));
if(Alist.get(i).equals("Q")){
Alist.remove("Q");
i--;
}
}
System.out.println(Alist);//[W, E, R]
增强的for循环遍历
//3.增强的for循环;
for(String demo:Alist){
}
System.out.println(Alist);//[Q, Q, Q, W, E, R]
在增强for循环中,实际上是不能进行对元素的删除操作(删除操作时会出现异常),如果要删除,只能删除一个,必须用break停止操作.
//增强for循环的删除操作;
for(String demo:Alist){
if(demo.equals("Q")){
Alist.remove(demo);
break;
}
}
System.out.println(Alist);//[Q, Q, W, E, R]
迭代器遍历1
用到了迭代器;
- iterator( )
//迭代器1
//public java.util.Iterator<E> iterator()
//首先获取迭代器对象;
Iterator<String> iterator=Alist.iterator();
//这里使用迭代器的hasNext方法,进行判断集合中还有没有下一个元素;
//public abstract boolean hasNext()
while (iterator.hasNext()){
//如果有就使用迭代器的next方法向下移动指针,并且返回指针指向的元素;
String s=iterator.next();
System.out.println(s);
}
在迭代器中可以进行删除元素的操作,使用迭代器的remove方法;删除元素;在删除后里面的游标(指针)会回退; 在迭代器中,cursor(指针)会记录遍历的元素序号.
//public java.util.Iterator<E> iterator()
Iterator<String> iterator=Alist.iterator();
//public abstract boolean hasNext()
while (iterator.hasNext()){
String s=iterator.next();
if(s.equals("Q")){
//使用迭代器的remove方法;删除元素;在删除后里面的游标(指针)会回退
iterator.remove();
}
}
System.out.println(Alist);//[W, E, R]
迭代器遍历2
jdk1.8更新之后;在List接口中特有的迭代器;只适用于List接口的实现类.
- ListIterator( )
返回值类型 | 方法 |
---|---|
void | add(E e) ;将指定的元素插入列表(可选操作)。 |
boolean | hasNext() ;返回 true如果遍历正向列表,列表迭代器有多个元素。 |
boolean | hasPrevious() ;返回 true如果遍历反向列表,列表迭代器有多个元素。 |
E | next() ;返回列表中的下一个元素,并且前进光标位置。 |
int | nextIndex() ;返回调用 next()返回的元素的索引。 |
E | previous() ;返回列表中的上一个元素,并向后移动光标位置。 |
int | previousIndex() ;返回由后续调用 previous()返回的元素的索引。 |
void | remove() ;从列表中删除由 next()或 previous()返回的最后一个元素(可选操作)。 |
void | set(E e) ;用指定的元素替换由 next()或 previous()返回的最后一个元素(可选操作)。 |
以上是关于JavaSE基础八----<集合>List接口及其实现类,List接口的迭代的主要内容,如果未能解决你的问题,请参考以下文章
JavaSE基础八----<集合>Set接口及其实现类,Set接口的迭代方式