ArrayList高级
equals()方法
判断两个ArrayList对象里面的元素是否全部相等,true(相等)、false(不相等)。
import java.util.ArrayList; import java.util.List; public class TestEquals { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("aaa"); List<String> list2 = new ArrayList<>(); list2.add("AAA"); //false Java区分大小写 System.out.println(list.equals(list2)); } }
isEmpty()方法
判断当前的集合是否为空,true(为空)、false(非空)。
import java.util.ArrayList; import java.util.List; public class TestIsEmpty { public static void main(String[] args) { List<String> list1 = new ArrayList<>(); //true:此时没有元素,结果true System.out.println(list1.isEmpty()); List<String> list2 = new ArrayList<>(); list2.add("Tomson"); //false:一旦有元素,则不为空,结果false System.out.println(list2.isEmpty()); } }
import java.util.List; public class TestArrayList { public static void main(String[] args) { List<String> list1 = new ArrayList<>(); // List<String> list = null; /* Exception in thread "main" java.lang.NullPointerException at com.whsxt.day6.arraylist.advance.TestArrayList.main(TestArrayList.java:10) */ // 没有创建对象,就调用成员方法会出现:NullPointerException // 也就是说:list为null,然后调用成员方法,抛出NullPointerException // System.out.println(list.isEmpty()); // 避免出现空指针异常,我们经常使用一下方式代替isEmpty()方法 if(null == list || list.size()==0) { System.out.println("is empty"); }else { System.out.println("不是空集合,可以对ArrayList做操作"); } } }
小结:isEmpty()方法会抛出NullPointerException,所以工作中使用 null == list || list.size()==0 来代替。
clear()方法
清空集合元素,但不会释放集合的内存。 在工作中clear()方法一般用于清空购物车。
import java.util.ArrayList; import java.util.List; public class TestArrayListClear { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Tom"); list.add("Tom"); list.add("Tom"); list.add("Tom"); list.add("Tom"); System.out.println(list.size()); // 调用该方法,回将ArrayList里面的数组所有的元素设置为null,但是不会释放ArrayList对象占用的内存,也就是说ArrayList这个对象还在 list.clear(); System.out.println("After="+list.size()); } }
indexOf()方法
返回某个元素在集合中的下标(索引),如果该元素在集合中不存在返回 -1。
import java.util.ArrayList; import java.util.List; public class TestArrayListIndexOf { /** * 创建集合对象,向集合添加元素 * 调用indexOf()方法,传入参数,判断参数是否在集合中存在 * @param args */ public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Tom"); list.add("Jim"); list.add("Sam"); int index = list.indexOf("Sam"); // 2 //int index = list.indexOf("Jerry"); // -1 System.out.println(index); } }
addAll()方法
将一个集合(A)的数据,加入到另一个集合(B)中。
import java.util.ArrayList; import java.util.List; public class TestArrayListAddAll { public static void main(String[] args) { List<String> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); list1.add("Tomson"); list2.add("Jerry"); list2.add("Jason"); // 将list1集合的数据加入到list2中 // addAll(Collection<? extends String>) 型参是父接口,实参可以传入子接口 // 放入集合中的数据必须是String或者String的子类 // 也就是说list1集合里面的数据必须是String或者String的子类(但是String没有子类,所以只能放String) list2.addAll(list1); System.out.println(list2); } }
数组集合之间的转换
asList():数组转集合
定义一个数组,然后转换为 ArrayList 集合。
import java.util.Arrays; import java.util.List; /** * 数组转换为集合 */ public class TestArrayToArrayList { public static void main(String[] args) { String[] arrs= {"Adam","Jackson","Martin"}; List<String> list = Arrays.asList(arrs); System.out.println(list); } }
import java.util.Arrays; import java.util.List; /** * 数组转换为集合 */ public class TestArrayToArrayList { public static void main(String[] args) { // int[] arrs = {10,20,40}; // 错误(不能使用基本类型的数据) Integer[] arrs = {10,20,40}; // list编译期是java.util.List类型的对象 // list运行期是java.util.Arrays.ArrayList,该类型没有add()方法,所以程序运行抛出UnsupportedOperationException异常 List<Integer> list = Arrays.asList(arrs); // UnsupportedOperationException:不支持add()方法所以抛出异常 // list.add(50); // list.remove(2); System.out.println(list.size()); } }
toArray():集合转数组
import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** *集合转数组 */ public class TestListToArray { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("Tomson"); list.add("Jackson"); list.add("Jason"); //将集合转换为Object类型数组 Object[] objArray = list.toArray(); System.out.println(Arrays.toString(objArray)); //如何将集合转换为String[]; String [] arrs =new String[list.size()]; //toArray(T[])型参是泛型数组,实参可以是类型明确的数组 //将arrs作为实参传入到toArray(T[])方法中,然后list将里面的元素填充到arrs数组 list.toArray(arrs); for(String str:arrs) { System.out.println(str); } } }
遍历ArrayList集合
遍历:访问集合中的每个元素。
import java.util.ArrayList; import java.util.List; import java.util.Iterator; /** * 遍历集合 */ public class TestIterator { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("tom1"); list.add("tom2"); list.add("tom3"); list.add("tom4"); list.add("tom5"); // 使用 for 循环,访问集合的每个元素 for (int i = 0; i < list.size(); i++) { // 根据下标获取集合的元素 String name = list.get(i); System.out.println(name); } // 使用 foreach 循环,访问集合的每个元素,不用使用下标遍历 // 从list集合里面获取每一个元素,将元素存储到变量name里面 // 由于没有下标,并且for循环的list只会执行一次,所以比for循环效率高 // : --> in for (String name : list) { System.out.println(name); } // 使用 Iterator(迭代器)访问集合每个元素 // it.hasNext():判断有没有元素,true有元素(进入循环体)、false没有元素 for(Iterator<String> it = list.iterator();it.hasNext();) { //获取元素 String name = it.next(); System.out.println("name="+name); } } }
其他方法
sort() 对集合中的元素排序。
subList(beginIndex,endIndex):根据开始索引和结束索引获取子集合的数据,数据取值范围 [beginIndex, endIndex) 。
降序排序 [1,0,10,20,5,8,30,25]
import java.util.ArrayList; import java.util.List; public class TestArrayListSort { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); list.add(0); list.add(10); list.add(20); list.add(18); /* list.sort(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); } });*/ // 使用lambda实现 list.sort((o1, o2) -> o1.compareTo(o2)); System.out.println(list); // 根据开始索引和结束索引获取子集合的数据 List<Integer> subList = list.subList(1, 3); subList.remove(0); System.out.println(subList); } }
LinkedList
双向链表:每个节点都有自己的前驱节点和后继节点
ArrayList是数组结构(线性),LinkedList是一个链表结构
LinkedList存储机制
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable { transient int size = 0; /** * 指向LinkedList的首部节点 */ transient Node<E> first; /** * 指向LinkedList的尾部节点 */ transient Node<E> last; //Node是LinkedList的静态内部类 //添加的元素全部放入Node对象 private static class Node<E> { ////E Element 存储对象的 //list.add(10); 此时10会存储到E item里面 E item; //当前节点的后继节点 Node<E> next; //当前节点的前驱节点 Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } } /**将新元素添加到链表的末尾*/ public boolean add(E e) { linkLast(e); return true; } /** * Links e as last element. 将e连接到链表的最后一个元素 [10,8] 需要将新节点9添加到集合 */ void linkLast(E e) { //last存储了添加新节点之前最后一个元素 Node=8 // l是一个局部变量,存储当前的最后一个节点信息 Node =8 final Node<E> l = last; //创建新节点对象Node=9,设置新节点的前驱节点 Node =8 final Node<E> newNode = new Node<>(l, e, null); //新节点创建完毕,新节点9就是LinkedList的最后一个节点 last = newNode; //条件成立:新节点是第一个节点 //条件不成立:将倒数第二个节点8的后继节点(next),指向新节点9 if (l == null) first = newNode; else //是指新节点的后继节点 l.next = newNode; size++; modCount++; } }
删除元素:只需要将当前节点(元素)的前驱节点和后继节点的关系解除,JVM帮你删除
LinkedList常用方法
boolean add(E): 新元素添加到集合的末尾
void addFirst(E): 新元素添加到集合的首部
void addLast(E): 新元素添加到集合的末尾
boolean offer(E): 新元素添加到集合末尾
boolean offerFirst(E): 新元素添加到集合首部
boolean offerLast(E): 新元素添加到集合末尾
add():返回类型boolean
offer():返回类型boolean
import java.util.LinkedList; /** * 新元素添加到集合末尾 */ public class TestLinkedListOffer { public static void main(String[] args) { LinkedList<Integer> list = new LinkedList<>(); list.offer(133); list.offer(132); list.offer(138); //[133, 132, 138] System.out.println(list); } }
get(int index):根据下标获取元素
getFirst():获取首部元素
getLast():获取尾部元素
peek():获取首部元素
peekFirst():获取首部元素
peekLast():获取尾部元素
区别:如果集合没有元素peek()相关方法返回null,get()相关方法抛出异常NoSuchElementException
import java.util.LinkedList; /** * peek()相关方法和get()相关方法都是获取元素 * 区别:如果集合没有元素peek()相关方法返回null,get()相关方法抛出异常NoSuchElementException */ public class TestLinkedListPeek { public static void main(String[] args) { LinkedList<Integer> list = new LinkedList<>(); list.offer(133); list.offer(132); list.offer(138); Integer num = list.peekFirst(); Integer num2=list.peekLast(); //peek():返回集合首部元素 Integer num3 = list.peek(); System.out.println("first="+num); System.out.println("last="+num2); System.out.println("first="+num3); } }
remove(int index):根据下标删除元素
removeFirst():删除首部元素
removeLast():删除尾部元素
poll():删除首部元素
pollFirst():删除首部元素
poolLast():删除尾部元素
区别:LinkedList集合没有元素的情况下,调用remove()相关方法删除元素会抛出NoSuchElementException,poll()相关方法会返回null。
共性:不管是remove()相关的方法还是poll()相关的方法,它们都是删除集合中的元素,会将删除的元素作为返回值。
LinkedList实现队列(queue)
队列:一个元素挨着一个元素
入队:offerLast()
出队:pollFirst()
特征:==先进先出==
【工作中经常使用于电商项目:提交订单会将购买信息放入到服务器的队列中(入队), 服务器处理你的订单(出队)。】
例:输入同学姓名,输入Q结束(使用栈完成)
import java.util.LinkedList; /** * 自定义队列 * 使用LinkedList实现的 */ public class TestQueue { /** * 队列的本质是一个LinkedList */ private LinkedList<String> queue = new LinkedList<>(); /** * 入队 * @param element 入队的元素 */ public void push(String element) { queue.offerFirst(element); } /** * 元素出队列,删除队列首部元素,返回删除的结果 * @return 删除的结果 */ public String poll() { return queue.pollLast(); } /** * 队列非空的情况下出队 * 出队列之前需要调用该方法进行判断,如果队列为空,不会调用出队列的方法poll() * @return true 队列非空 false队列为空 */ public boolean isNotEmpty() { return !(null == queue || queue.size()==0); } }
import java.util.Scanner; /** * 测试入队和出队 * 场景:输入同学姓名,输入Q结束,入队列 offerLast 新元素添加到集合末尾 [Tom,Jerry,Martin,Adam,Robert] * 打印同学的姓名 出队列 pollFirst [Tom,Jerry,Martin,Adam,Robert] * 前提:非空的情况下才能出队列 */ public class TestQueue { /** * 步骤: * 1创建队列 * 2使用循环:输入姓名,没有输入Q的情况下,一直入队,一旦输入Q停止入队 * 3使用循环:队列非空的情况下出队 * @param args */ public static void main(String[] args) { try(Scanner input = new Scanner(System.in);){ J0812Queue queue = new J0812Queue(); System.out.println("请输入学生姓名,输入Q结束"); //入队 while(true) { String name = input.nextLine(); //条件成立:停止入队 if("Q".equalsIgnoreCase(name)) { break; } queue.push(name); } //出队 while(queue.isNotEmpty()) { String name = queue.poll(); System.out.print(name+" "); } }catch(Exception e) { System.err.println("队列操作失败"); e.printStackTrace(); } } }
3.4LinkedList实现栈
特征:==先进后出 ,后进先出==(类似于弹夹进出子弹)
入栈:offerLast()
出栈:pollLast()
/** * 入栈 * @param element 入栈的元素 */ public void push(String element) { queue.offerLast(element); } /** * 元素出栈,删除栈尾部元素,返回删除的结果 * @return 删除的结果 */ public String poll() { return queue.pollLast(); }
ArrayList和LinkedList区别
1、ArrayList本质是一个数组,LinkedList本质是一个链表
2、ArrayList构造方法有一个int类型的参数,表示数组的长度。LinkedList构造方法没有int类型的参数
3、ArrayList遍历元素效率高,删除首部和中间元素效率低。LinkedList删除首部和中间元素效率高,遍历元素效率低
4、ArrayList的优点就是LinkedList缺点,LinkedList优点就是ArrayList缺点。
ArrayList和LinkedList单元测试(20W的压力)
集合名称 | add()方法耗时 | get()方法耗时 | 删除首部remove()方法耗时 |
---|---|---|---|
ArrayList | 3.67ms | 3.33ms | 1.72s(后面元素依次往前搬家,所以耗时) |
LinkedList | 2ms | 14.6s(没有下标,二分查找,所以耗时) | 6.33ms |
总体:ArrayList 性能高于LinkedList
小结:经常遍历集合中的元素使用 ArrayList
经常需要插入和删除集合中的元素使用 LinkedList
import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.junit.Test; /** * ArrayList add get remove 20W * LinkedList add get remove 20W */ public class TestArrayListAndLinkedList { private List<Integer> arrayList = new ArrayList<>(3000); private List<Integer> linkedList = new LinkedList<>(); private final static int SIZE = 200000; /** * 3.67ms */ @Test public void testArrayListAdd() { for(int i=0;i<SIZE;i++) { arrayList.add(i); } System.out.println(arrayList.size()); } /** * 2ms */ @Test public void testLinkedListAdd() { for(int i=0;i<SIZE;i++) { linkedList.add(i); } System.out.println(linkedList.size()); } /** * 测试ArrayList的get方法20W * 1. 调用add方法添加元素到集合 * 2. 使用for循环get20W次 * 耗时3.33ms */ @Test public void testArrayListGet() { this.testArrayListAdd(); for(int i=0;i<SIZE;i++) { arrayList.get(i); } } /** * 测试LinkedList的get方法20W * 1. 调用add方法添加元素到集合 * 2. 使用for循环get20W次 * 耗时14.6秒 */ @Test public void testLinkedListGet() { this.testLinkedListAdd(); for(int i=0;i<SIZE;i++) { linkedList.get(i); } } /** * 测试ArrayListremove20W * 1. 调用add方法添加元素到集合 * 2. 使用for循环删除ArrayList首部元素 * 耗时1.72秒 */ @Test public void testArrayListRemove() { this.testArrayListAdd(); for(int i=0;i<SIZE;i++) { arrayList.remove(0); } } /** * 测试LinkedList remove20W * 1. 调用add方法添加元素到集合 * 2. 使用for循环删除LinkedList首部元素 * 耗时6.33ms */ @Test public void testLinkedListRemove() { this.testLinkedListAdd(); for(int i=0;i<SIZE;i++) { linkedList.remove(0); } } }
ArrayList的排序
1、Comparable 内部比较器
2、Comparator 外部比较器
3、Collator 汉字按照拼音排序
4、Collections.reverseOrder(Comparator); 降序排序
需要:Collections工具类的sort()方法
还需要:Comparable和Comparator比较器
Comparable 内部比较器,实体类通常实现该接口来制定比较规则,也是一个默认的比较规则。
Comparator 外部比较器,也是一个比较工具的接口 。
场景:定义一个ArrayList集合,里面存储了若干个Student类型的对象{id,stuName,stuAge,stuScore}
问题1:默认按照id排序
步骤:1. 定义Student类型,实现Comparable接口
2. 定义ArrayList集合,将Student类型添加到集合中
3. 调用Collections.sort(list)进行排序
public class Student implements Comparable<Student> { private int id; private String stuName; private int stuAge; /** * 学生成绩 */ private int stuScore; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getStuName() { return stuName; } public void setStuName(String stuName) { this.stuName = stuName; } public int getStuAge() { return stuAge; } public void setStuAge(int stuAge) { this.stuAge = stuAge; } public int getStuScore() { return stuScore; } public void setStuScore(int stuScore) { this.stuScore = stuScore; } public Student() {} public Student(int id, String stuName, int stuAge, int stuScore) { this.id = id; this.stuName = stuName; this.stuAge = stuAge; this.stuScore = stuScore; } @Override public String toString() { return "Student [id=" + id + ", stuName=" + stuName + ", stuAge=" + stuAge + ", stuScore=" + stuScore + "]"; } /** *Comparable比较器实现的方法,我们在此自定义比较的规则 *对id进行比较 */ @Override public int compareTo(Student o) { /* if( this.getId()>o.getId()){ return 1; }else if(this.getId()<o.getId()) { return -1; }else { return 0; } */ return this.getId() - o.getId(); } }
import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * 创建ArrayList对象,将Student添加到ArrayList,将其按照id排序,最后打印排序结果 */ public class TestStudentSort { private final static List<Student> STUDENT_LIST = new ArrayList<>(); static { STUDENT_LIST.add(new Student(19, "Sam", 18,39)); STUDENT_LIST.add(new Student(13, "Merry", 16,79)); STUDENT_LIST.add(new Student(14, "Terry", 31,89)); STUDENT_LIST.add(new Student(18, "Carry", 18,69)); STUDENT_LIST.add(new Student(29, "Jerry", 19,59)); STUDENT_LIST.add(new Student(15, "Herry", 17,49)); } public static void main(String[] args) { // 将STUDENT_LIST集合排序 // 编译错误:因为没有指定比较的规则,也就是说sort方法进行排序,必须指定比较规则 // Collections.sort(STUDENT_LIST); Collections.sort(STUDENT_LIST); // 遍历集合元素 for(Student stu : STUDENT_LIST) { System.out.println(stu); } } }
注意:排序必须要指定比较规则(可以用Comparable和Comparator来指定).
问题2:从控制台输入整数【1按照id排序,2按照年龄排序,3按照成绩排序,4按照姓名排序】
import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Scanner; /** * 创建ArrayList对象,将Student添加到ArrayList,将其按照id排序,最后打印排序结果 */ public class TestStudentSort2 { private final static List<Student> STUDENT_LIST = new ArrayList<>(); static { STUDENT_LIST.add(new Student(19, "刘大", 18,39)); STUDENT_LIST.add(new Student(13, "陈二", 16,79)); STUDENT_LIST.add(new Student(14, "张三", 31,89)); STUDENT_LIST.add(new Student(18, "李四", 21,69)); STUDENT_LIST.add(new Student(29, "王五", 19,59)); STUDENT_LIST.add(new Student(15, "赵六", 17,49)); } /** * 步骤:1. 从控制台输入整数 * 2. 编写switch语句 * 3. 根据客户输入整数进行对应的排序策略 * @param args */ public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.println("请输入整数1按照id排序2按照年龄排序3按照成绩排序4按照姓名排序"); switch (input.nextInt()) { case 1: Collections.sort(STUDENT_LIST); break; case 2: // 匿名内部类实现 Collections.sort(STUDENT_LIST,new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getStuAge()-o2.getStuAge(); } }); break; case 3: // lambda实现 Collections.sort(STUDENT_LIST,(o1,o2)->o1.getStuScore()-o2.getStuScore()); break; case 4: Collections.sort(STUDENT_LIST,(o1,o2)->o1.getStuName().compareTo(o2.getStuName())); break; default: System.out.println("输入错误,程序退出"); break; } //遍历集合元素 for(Student stu : STUDENT_LIST) { System.out.println(stu); } } }
中文排序
import java.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Scanner; /** * 创建ArrayList对象,将Student添加到ArrayList,将其按照id排序,最后打印排序结果 */ public class TestStudentSort2 { private final static List<Student> STUDENT_LIST = new ArrayList<>(); static { STUDENT_LIST.add(new Student(19, "刘大", 18,39)); STUDENT_LIST.add(new Student(13, "陈二", 16,79)); STUDENT_LIST.add(new Student(14, "张三", 31,89)); STUDENT_LIST.add(new Student(18, "李四", 21,69)); STUDENT_LIST.add(new Student(29, "王五一", 19,59)); STUDENT_LIST.add(new Student(15, "赵六", 17,41)); } /** * 步骤:1. 从控制台输入整数 * 2. 编写switch语句 * 3. 根据客户输入整数进行对应的排序策略 * @param args */ public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.println("请输入整数1按照id排序2按照年龄排序3按照成绩排序4按照姓名排序"); switch (input.nextInt()) { case 1: Collections.sort(STUDENT_LIST); break; case 2: Collections.sort(STUDENT_LIST,new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getStuAge()-o2.getStuAge(); } }); break; case 3: //Collections.sort(STUDENT_LIST,(o1,o2)->o1.getStuScore()-o2.getStuScore()); //使用降序对成绩进行排序 //代码可读性不佳 //Collections.sort(STUDENT_LIST,(o1,o2)->-(o1.getStuScore()-o2.getStuScore())); //Collections.reverseOrder(cmp) 将集合中的元素进行反转排序 Collections.sort(STUDENT_LIST, Collections.reverseOrder((o1,o2)->o1.getStuScore()-o2.getStuScore())); break; case 4: /* // Collections.sort(STUDENT_LIST,(o1,o2)->o1.getStuName().compareTo(o2.getStuName())); // 对汉字进行排序(按照拼音排序) Collections.sort(STUDENT_LIST, new Comparator<Student>() { // 1.创建Collator对象 // 2.调用该对象的compare(o1,o2)方法进行排序 @Override public int compare(Student o1, Student o2) { // 创建Collator对象 此类来构建自然语言文本的搜索和排序例程 // Collator collator = Collator.getInstance(); Collator collator = Collator.getInstance(Locale.CHINA); return collator.compare(o1.getStuName(), o2.getStuName()); } }); Collections.sort(STUDENT_LIST, (o1, o2) -> { Collator collator = Collator.getInstance(); return collator.compare(o1.getStuName(), o2.getStuName()); }); //Collator实现类Comparator接口,所以你首先要创建Collator类型的对象 //然后调用compare(str1,str2)方法,参数就是你要指定的字符串比较规则 */ Collections.sort(STUDENT_LIST,(o1,o2)->Collator.getInstance(Locale.CHINA).compare(o1.getStuName(), o2.getStuName())); break; default: System.out.println("输入错误,程序退出"); break; } //遍历集合元素 for(Student stu : STUDENT_LIST) { System.out.println(stu); } } }