集合(二)
关于集合元素的remove
迭代过程中,不能直接通过集合删除元素,要通过迭代器删除元素
package se3.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTest06 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(1);
c.add(2);
c.add(3);
Iterator it = c.iterator();
while (it.hasNext()){
//编写代码时next()方法返回值类型必须是Object
Object obj = it.next();
//c2.remove();//直接通过集合去删除元素,没有通知迭代器,会报错
//使用迭代器来删除
it.remove();//删除的一定是迭代器指向的当前元素
System.out.println(obj);
}
System.out.println(c.size());//0
}
}
List接口特有方法
测试List接口中常用方法
-
List集合存储元素特点:有序可重复
有序:List集合中的元素有下标,从0开始,以1递增
可重复:存储一个1,还可以在存储1
-
List既然是Collection接口的子接口,那么肯定List接口有自己“特色”的方法:
以下只列出List接口常用的方法
void add(int index,Object element)
Object get(int index)
int indexOf(Object o)
int lastIndexOf(Object o)
Object remove(int index)
Object set(int index,Object element)
package se3.list;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class ListTest01 {
public static void main(String[] args) {
//创建List类型的集合
List myList = new ArrayList();
//List myList = new LinkedList();
//添加元素
myList.add("A");//默认都是向集合末尾添加元素
myList.add("B");
myList.add("C");
myList.add("C");
myList.add("D");
//在指定位置添加指定元素(第一个参数是下标)
//使用不多,对ArrayList来说效率比较低
myList.add(1,"KING");
//迭代
Iterator it = myList.iterator();
while (it.hasNext()){
Object obj = it.next();
System.out.print(obj + " ");
}
//运行结果:A KING B C D
System.out.println();
//根据下标获取元素
Object obj1 = myList.get(0);
System.out.println(obj1);//A
//因为有下标,所以list集合有自己比较特殊的遍历方式
//通过下标遍历(List集合特有的方式,Set没有)
for (int i = 0; i < myList.size(); i++) {
Object object = myList.get(i);
System.out.print(object + " ");
}//运行结果:A KING B C D
System.out.println();
//获取指定对象第一次出现处的索引
System.out.println(myList.indexOf("KING"));//1
//获取指定对象最后一次出现处的索引
System.out.println(myList.lastIndexOf("C"));//4
//删除指定下标的元素
//删除下标为0的元素
myList.remove(0);
System.out.println(myList.size());//5
//修改指定位置的元素
myList.set(2,"Soft");
//遍历
for (int i = 0; i < myList.size(); i++) {
Object object = myList.get(i);
System.out.print(object + " ");
}
//运行结果:KING B Soft C D
}
}
ArrayList集合
ArrayList集合初始化容量及扩容
1. 默认初始化容量是10(底层先创建长度为0的数组,当添加第一个元素的时候,初始化容量为10)
2. 集合底层是一个Object[]数组
3. ArrayList集合的扩容
原容量的1.5倍
ArrayList集合底层是数组,怎么优化?
尽可能少的扩容,因为数组扩容效率低,建议使用ArrayList集合的时候预估计元素的个数,给定一个初始化容量。
4. 数组优点:检索效率高
5. 数组缺点:随机增删元素效率较低,以及数组无法存储大数据量(很难找到一块大的连续的存储空间)
6. 向数组末尾添加元素,效率很高,不受影响
7. 面试:这么多集合中,你用哪个集合最多?
答:ArrayList集合,因为往数组末尾添加元素效率不受影响,另外,我们检索/查找某个元素的操作比较多。
8.ArrayList集合是非线程安全的
package se3.list;
import java.util.ArrayList;
import java.util.List;
public class ArrayListTest01 {
public static void main(String[] args) {
//默认初始化容量是10
List list1 = new ArrayList<>();
System.out.println(list1.size());//0
//指定初始化容量:20(数组长度是20)
List list2 = new ArrayList(20);
//集合的size()方法是获取当前集合中元素的个数,不是获取集合的容量
System.out.println(list2.size());//0
list1.add(1);
list1.add(2);
list1.add(3);
list1.add(4);
list1.add(5);
list1.add(6);
list1.add(7);
list1.add(8);
list1.add(9);
list1.add(10);
System.out.println(list1.size());
//再加一个元素
list1.add(11);
//这里容量扩大为原来的1.5倍(自动扩容)
System.out.println(list1.size());//11
}
}
构造方法
- new ArrayList();
- new ArrayList(20);
package se3.list;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
//集合ArrayList的构造方法
public class ArrayListTest02 {
public static void main(String[] args) {
//默认初始化容量10
List myList1 = new ArrayList();
//指定初始化容量100
List myList2 = new ArrayList(100);
//创建一个HashSet集合
Collection c = new HashSet();
//添加元素到Set集合
c.add(100);
c.add(200);
c.add(900);
c.add(50);
//通过这个构造方法就可以将HashSet集合转换成List集合
List myList3 = new ArrayList(c);
for (int i = 0; i < myList3.size(); i++) {
System.out.println(myList3.get(i));
}
/*运行结果:
50
100
900
200
*/
}
}
单向链表数据结构
对于链表数据结构来说:基本的单元是节点Node
对于单向链表来说,任何一个节点Node中都有两个属性:
- 存储的数据
- 下一节点的内存地址
单向链表示意图
增加元素图
删除元素图
链表的优点与缺点
- 优点:随机增删元素效率较高。(因为增删元素不涉及到大量元素位移)。如果遇到随机增删集合中元素的业务比较多时,建议使用LinkedList。
- 缺点:查询效率较低,每一次查找某个元素的时候都需要从头节点开始往下遍历。
ArrayList:把检索发挥到极致
LinkedList:把随机增删发挥到极致
package se3.list;
import java.util.LinkedList;
import java.util.List;
public class LinkedListTest01 {
public static void main(String[] args) {
/**
* LinkedList集合底层也是有下标的
* 注意:ArrayList之所以检索效率比较高,不是单纯因为下标的原因。是因为底层数组发挥的作用。
* LinkedList集合照样有下标,但是检索/查找某个元素的时候效率比较低,因为只能从头节点一个一个遍历。
*/
List list = new LinkedList();
list.add("a");
list.add("b");
list.add("c");
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
System.out.print(obj + " ");
}
//输出结果:a b c
}
}
双向链表与LinkedList集合
双向链表的基本单元还是节点Node
双向链表结构图
package se3.list;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class LinkedListTest01 {
public static void main(String[] args) {
/**
* LinkedList集合底层也是有下标的
* 注意:ArrayList之所以检索效率比较高,不是单纯因为下标的原因。是因为底层数组发挥的作用。
* LinkedList集合照样有下标,但是检索/查找某个元素的时候效率比较低,因为只能从头节点一个一个遍历。
*/
List list = new LinkedList();
list.add("a");
list.add("b");
list.add("c");
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
System.out.print(obj + " ");
}
//输出结果:a b c
/**
* LinkedList集合有初始化容量吗?没有
* 不管是LinkedList还是ArrayList,以后不需要关心具体是哪个集合
* 因为我们要面向接口编程,调用的方法都是接口中的方法
*/
System.out.println();
List list2 = new ArrayList();//这样写表示底层用了数组
//List list2 = new LinkedList();//这样写表示底层用了双向链表
list2.add("123");
list2.add("456");
list2.add("789");
for (int i = 0; i < list2.size(); i++) {
System.out.print(list2.get(i) + " ");
}
//输出结果:123 456 789
}
}
Vector集合
package se3.list;
import java.util.*;
/**Vector
* 1.底层也是一个数组
* 2.初始化容量:10
* 3.怎么扩容的?
* Vector集合扩容的特点:10-->20-->40-->80
*扩容之后是原容量的2倍
* 4.ArrayList集合扩容特点:10-->15-->15*1.5
* ArrayList扩容是原容量的1.5倍
* 5.Vector中所有的方法都是线程同步的,都带有synchronized关键字
* 是线程安全的。效率比较低,使用较少了。
*/
public class VectorTest {
public static void main(String[] args) {
//创建一个集合
List v = new Vector();
//Vector v = new Vector();
//默认容量10
v.add(1);
v.add(2);
v.add(3);
v.add(4);
v.add(5);
v.add(6);
v.add(7);
v.add(8);
v.add(9);
v.add(10);
//满了之后扩容(扩容之后是20)
v.add(11);
Iterator it = v.iterator();
while (it.hasNext()){
Object object = it.next();
System.out.print(object + " ");
}//输出结果;1 2 3 4 5 6 7 8 9 10 11
/*
怎么将一个线程不安全的ArrayList集合转换成线程安全的呢?
使用集合工具类:java.util.Collections;
java.util.Collection是集合接口
java.util.Collections是集合工具类
*/
List mylist = new ArrayList();//非线程安全的
//变成线程安全的
Collections.synchronizedList(mylist);
//myList集合就是线程安全的了
mylist.add("111");
mylist.add("222");
mylist.add("333");
}
}
泛型
泛型这种语法机制,只在程序编译阶段起作用,只是给编译器看的。(运行阶段泛型没用)
使用泛型的优点:
- 集合中存储的元素类型统一了
- 从集合中取出的元素类型是泛型指定的类型,不需要进行大量的向下转型
缺点
泛型的缺点:导致集合中存储的元素缺乏多样性!
注意:大多数业务中,集合的元素类型还是统一的。
package se3.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericTest01 {
public static void main(String[] args) {
//不使用泛型机制,分析程序存在的缺点
List myList = new ArrayList();
//准备对象
Cat c = new Cat();
Bird b = new Bird();
//将对象添加到集合当中
myList.add(c);
myList.add(b);
//遍历集合,取出Cat让她抓老鼠,取出Bird让她飞!
Iterator it = myList.iterator();
while (it.hasNext()){
Object obj = it.next();
//强制转型
if (obj instanceof Animal){
Animal a = (Animal)obj;
a.move();
}
if(obj instanceof Cat){
Cat c1 = (Cat)obj;
c1.catMouse();
}
if(obj instanceof Bird){
Bird b1 = (Bird)obj;
b1.fly();
}
}
//输出结果:
//动物在移动!
//猫在抓老鼠!
//动物在移动!
//鸟儿在飞翔!
}
}
class Animal{
//父类自带的方法
public void move(){
System.out.println("动物在移动!");
}
}
class Cat extends Animal{
//特有方法
public void catMouse(){
System.out.println("猫在抓老鼠!");
}
}
class Bird extends Animal{
//特有方法
public void fly(){
System.out.println("鸟儿在飞翔!");
}
}
package se3.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericTest02 {
public static void main(String[] args) {
//使用泛型List<Animal2>之后,表示List集合中只允许存储Animal类型的数据
//用泛型来指定集合中数据存储的数据类型
List<Animal2> myList2 = new ArrayList<Animal2>();
//指定list集合在只能存储Animal
//myList2.add("abc");报错
Cat2 c2 = new Cat2();
Bird2 b2 = new Bird2();
myList2.add(c2);
myList2.add(b2);
//获取迭代器
//这个表示迭代器迭代的是Animal类型
Iterator<Animal2> it2 = myList2.iterator();
while (it2.hasNext()){
//使用泛型之后,每一次迭代返回的数据类型都是Animal类型
Animal2 a2 = it2.next();
//如果直接使用Animal类型,不需要进行强制类型转换了,直接调用
//a2.move();
if(a2 instanceof Cat2){
Cat2 x = (Cat2)a2;
x.catMouse();
}
if (a2 instanceof Bird2){
Bird2 y = (Bird2)a2;
y.fly();
}
}
//输出结果:
//猫在抓老鼠!
//鸟儿在飞翔!
}
}
class Animal2{
//父类自带的方法
public void move(){
System.out.println("动物在移动!");
}
}
class Cat2 extends Animal2{
//特有方法
public void catMouse(){
System.out.println("猫在抓老鼠!");
}
}
class Bird2 extends Animal2{
//特有方法
public void fly(){
System.out.println("鸟儿在飞翔!");
}
}
自动类型推断
JDK8之后引入了:自动类型推断机制(又称钻石表达式)
package se3.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericTest03 {
public static void main(String[] args) {
//ArrayList<这里的类型会自动推断,前提是JDK8之后才允许>
//自动类型推断
//List<Animal2> myList3 = new ArrayList<Animal2>();
List<Animal2> myList3 = new ArrayList<>();
myList3.add(new Animal2());
myList3.add(new Cat2());
myList3.add(new Bird2());
//遍历
Iterator<Animal2> it3 = myList3.iterator();
while (it3.hasNext()){
Animal2 aa = it3.next();
aa.move();
}
List<String> stringList = new ArrayList<>();
//类型不匹配
//stringList.add(new Cat2());
//stringList.add(123);
stringList.add("http://www.163.com");
stringList.add("http://www.baidu.com");
stringList.add("http://www.bjpownode.com");
Iterator<String> its = stringList.iterator();
while (its.hasNext()){
String s = its.next();
String newString = s.substring(7);
System.out.println(newString);
}
/**
* 运行结果:
* 动物在移动!
* 动物在移动!
* 动物在移动!
* www.163.com
* www.baidu.com
* www.bjpownode.com
*/
}
}
自定义泛型
自定义泛型的时候,<>(尖括号)中的是一个标识符,随便写
java源码中经常出现的是和
1.E是Element单词首字母
2.T是Type单词首字母
package se3.collection;
public class GenericTest04<标识符随便写> {
public void doSome(标识符随便写 o){
System.out.println(o);
}
public static void main(String[] args) {
GenericTest04<String> gt = new GenericTest04<>();
//gt.doSome(100);报错:类型不匹配
gt.doSome("qwq");//输出qwq
//==========================================
GenericTest04<Integer> gt2 = new GenericTest04<>();
//gt2.doSome("阿波");报错:类型不匹配
gt2.doSome(100);//输出100
MyIterator<String> mi = new MyIterator<>();
String s1 = mi.get();
//Animal aaa2 = mi2.get();报错
MyIterator<Animal> mi2 = new MyIterator<>();
Animal aaa = mi2.get();
//String s2 = mi2.get();报错
//不用泛型就是Object类型
GenericTest04 gt3 = new GenericTest04();
gt3.doSome(new Object());
gt3.doSome(200);
gt3.doSome("阿波");
}
}
class MyIterator<T>{
public T get(){
return null;
}
}
集合使用foreach
package se3.collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ForEachTest01 {
public static void main(String[] args) {
//创建List集合
List<String> stringList = new ArrayList<>();
//添加元素
stringList.add("hello");
stringList.add("阿波");
stringList.add("123");
//遍历,使用迭代器方式
Iterator<String> it = stringList.iterator();
while (it.hasNext()){
String s = it.next();
System.out.print(s + " ");
}
System.out.println();
//使用下标的方式(只针对于有下标的集合)
for (int i = 0; i < stringList.size(); i++) {
System.out.print(stringList.get(i) + " ");
}
System.out.println();
//使用foreach
for (String s : stringList) {//因为泛型使用的是Sting类型,所以是String s
System.out.print(s + " ");
}
System.out.println();
List<Integer> list = new ArrayList<>();
list.add(100);
list.add(200);
list.add(300);
for (Integer i : list){//i代表集合中的元素
System.out.print(i + " ");
}
/**
* 输出结果:
* hello 阿波 123
* hello 阿波 123
* hello 阿波 123
* 100 200 300
*/
}
}