集合体系结构
Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
collection集合体系
Collection的常用方法
package com.itchinajie.d1_collection;import java.util.ArrayList;
import java.util.HashSet;/*
* 目标:认识Collection体系的特点。
* */
public class CollectionTest1 {public static void main(String[] args) {//简单确认一下Collection集合的特点。ArrayList<String> list=new ArrayList<>();//有序 可重复 有索引list.add("java1");list.add("java2");list.add("java1");list.add("java2");System.out.println(list);HashSet<String>set = new HashSet<>();set.add("java1");set.add("java2");set.add("java1");set.add("java2");set.add("java3");System.out.println(set);}
}package com.itchinajie.d1_collection;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;public class CollectionTest2API {public static void main(String[] args) {Collection<String> c = new ArrayList<>();// 多态写法//1.public boolean add(Ee):添加元素,添加成功返回true。c.add("java1");c.add("java1");c.add("java2");c.add("java2");c.add("java3");System.out.println(c);//2.public void clear():清空集合的元素。//c.clear();//system.out.println(c);//3.public boolean isEmpty():判断集合是否为空是空返回true,反之。System.out.println(c.isEmpty()); // false//4.public int size():获取集合的大小。System.out.println(c.size());//5.public boolean contains(object obj):判断集合中是否包含某个元素。System.out.println(c.contains("java1")); // trueSystem.out.println(c.contains("Java1")); // false//6.public boolean remove(Ee):删除某个元素:如果有多个重复元素默认删除前面的第一个!System.out.println(c.remove( "java1"));System.out.println(c);//7.public Object [] toArray();把集合转换成数组Object[] arr = c.toArray();System.out.println(Arrays.toString(arr));String[] arr2 = c.toArray(new String[c.size()]);System.out.println(Arrays.toString(arr2));System.out.println("------------------------------------");//把一个集合的全部数据倒入到另一个集合中去。Collection<String>c1 = new ArrayList<>();c1.add("java1");c1.add("java2");Collection<String> c2 = new ArrayList<>();c2.add("java3");c2.add("java4");c1.addAll(c2);//就是把c2集合的全部数据倒入到c1集合中去。System.out.println(c1);System.out.println(c2);}
}
Collection的遍历方式
迭代器
迭代器是用来遍历集合的专用方式(数组没有迭代器),在到ava中迭代器的代表是Iterator。
package com.itchinajie.d2_collection_traverse;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/*
* 目标:Collection集合遍历方式一:使迭代器Iterator遍历
* */
public class CollectionDemo1 {public static void main(String[] args) {Collection<String> c = new ArrayList<>();c.add("赵敏");c.add("小昭");c.add("素素");c.add("灭绝");System.out.println(c);//c=[赵敏,小昭,素素,灭绝]// it//使用迭代器遍历集合//1、从集合对象中获取迭代器对象。Iterator<String> it = c.iterator();
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());//System.out.println(it.next());//出现异常的//2、我们应该使用循环结合迭代器遍历集合。while (it.hasNext()){
// String ele = it.next();
// System.out.println(ele);System.out.println(it.next());}}
}
增强for
增强for可以用来遍历集合或者数组。
增强for遍历集合,本质就是迭代器遍历集合的简化写法。
package com.itchinajie.d2_collection_traverse;import java.util.ArrayList;
import java.util.Collection;/** 目标:Collection集合遍历方式二:增强for* */
public class CollectionDemo2 {public static void main(String[] args) {Collection<String> c = new ArrayList<>();c.add("赵敏");c.add("小昭");c.add("素素");c.add("灭绝");System.out.println(c);//c=[赵敏,小昭,素素,灭绝]// ele//使用增强for遍历集合或者数组。for (String ele : c){System.out.println(ele);}String[] names = {"迪丽热巴","古力娜扎","稀奇哈哈"};for (String name : names){System.out.println(name);}}
}
Lambda表达式
得益于刊DK8开始的新技术Lambda表达式,提供了一种更简单、更直接的方式来遍历集合。
package com.itchinajie.d2_collection_traverse;import java.util.ArrayList;
import java.util.Collection;/*
* 目标:Collection集合的遍历方式三:JDK8开始新增的lambda表达式(forEach方法)
* */
public class CollectionDemo3 {public static void main(String[] args) {Collection<String> c = new ArrayList<>();c.add("赵敏");c.add("小昭");c.add("殷素素");c.add("周芷若");System.out.println(c);//[赵敏,小昭,般素素,周芷若]// s//default void forEach(Consumer<?superT>action): 结合Lambda表达式遍历集合:// c.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
// //简化
// c.forEach((String s) -> {
// System.out.println(s);
// });//简化
// c.forEach( s -> {
// System.out.println(s);
// });//简化
// c.forEach( s -> System.out.println(s));c.forEach( System.out::println);}
}
List系列集合
List集合的特有方法 List集合因为支持索引,所以多了很多与索引相关的方法,当然,Collection的功能List也都继承了。
List集合的特有方法
package com.itchinajie.d3_collection_list;import java.util.ArrayList;
import java.util.List;/**目标:掌握List系列集合的特点,以及其提供的特有方法。*/
public class ListTest1 {public static void main(String[] args) {//1.创建一个ArrayList集合对象(有序、可重复、有索引)List<String> list = new ArrayList<>();// 一行经典代码list.add("蜘蛛精");list.add("至尊宝");list.add("至尊宝");list.add("牛夫人");System.out.println(list);//【蜘蛛精,至尊宝,至尊宝,牛夫人]//2.public void add(int index,E element):在某个索引位置插入元素。list.add( 2,"紫霞仙子");System.out.println(list);//3.public E remove(int index):根据索引删除元素,返回被删除元素System.out.println(list.remove(2));System.out.println(list);//4.public E get(int index):返回集合中指定位置的元素。System.out.println(list.get(3));//5.public E set(int index,E element):修改索引位置处的元素,修改成功后,会返回原来的数据System.out.println(list.set(3,"牛魔王"));System.out.println(list);}
}
遍历方式
List集合支持的遍历方式:
1、for循环(因为List集合有索引)
2、迭代器
3、增强for循环(foreach遍历)
4、Lambda表达式
package com.itchinajie.d3_collection_list;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;/*
* List集合支持的遍历方式:
1、for循环(因为List集合有索引)
2、迭代器
3、增强for循环
4、Lambda表达式
* */
public class ListTest2 {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("糖宝宝");list.add("蜘蛛精");list.add("至尊宝");//(1)for循环for (int i=0;i<list.size();i++){//i=012String s = list.get(i);System.out.println(s);}//(2)迭代器Iterator<String> it = list.iterator();while (it.hasNext()){System.out.println(it.next());}//(3)增强for循环(foreach循环)for (String s : list) {System.out.println(s);}//(4)JDK1.8开始之后的Lambda表达式list.forEach(s -> {System.out.println(s);});}
}
ArrayList集合的底层原理
特点
1、基于数组实现的
2、查询速度快(注意:是根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同。
3、删除效率低:可能需要把后面很多的数据进行前移。
4、添加效率极低:可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。
底层原理
1、利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组;
2、添加第一个元素时,底层会创建一个新的长度为10的数组;
3、存满时,会扩容1.5倍;
4、如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准。
ArrayList集合的应用原理
1、ArrayList适合:根据索I查询数据 ,比如根据随机索引数据(高效)!或者数据量 不是很大时!
2、ArrayList不适合:数据量大的同时 ,又要频繁的进行增删操作!
LinkedList集合的底层原理
基于双链表实现的。
什么是链表?有啥特点?
单项链表:链表的特点1: 查询慢,无论查询哪个数据都要从头开始找;
链表的特点2: 链表增删相对快。
双向链表(基于双链表实现):基于双链表实现的。
特点:查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的。
新增首尾操作的特有方法
LinkedList集合的应用场景
1、用来设计队列
2、用来设计栈
package com.itchinajie.d3_collection_list;import java.util.LinkedList;/**目标:掌握List系列集合的特点,以及其提供的特有方法。*/
public class ListTest3 {public static void main(String[] args) {//1、创建一个队列。LinkedList<String> queue = new LinkedList<>();//入队queue.addLast("第1号人");queue.addLast("第2号人");queue.addLast("第3号人");queue.addLast("第4号人");System.out.println(queue);//出队System.out.println(queue.removeFirst());System.out.println(queue.removeFirst());System.out.println(queue.removeFirst());System.out.println(queue);System.out.println("------------------------------------");//2、创建一个栈对象。LinkedList<String> stack = new LinkedList<>();//压栈
// stack.addFirst("第1颗子弹");
// stack.addFirst("第2颗子弹");
// stack.addFirst("第3颗子弹");
// stack.addFirst("第4颗子弹");stack.push("第1颗子弹");stack.push("第2颗子弹");stack.push("第3颗子弹");stack.push("第4颗子弹");System.out.println(stack);//出栈
// System.out.println(stack.removeFirst());
// System.out.println(stack.removeFirst());System.out.println(stack.pop());System.out.println(stack.pop());System.out.println(stack);}
}
set系列集合
package com.itchinajie.d4_collection_set;import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;/*
* 目标:整体了解一下Set系列集合的特点
* */
public class SetTest1 {public static void main(String[] args) {//1、创建一个Set集合的对象//Set<Integer>set =new HashSet<>();//创建了一个HashSet的集合对象。 一行经典代码 HashSet:无序不重复无索引,// 不支持索引操作,一般只会无序一次// Set<Integer> set = new LinkedHashSet<>();//有序 不重复 无索引Set<Integer> set =new TreeSet<>();//可排序(升序) 不重复 无索引set.add(666);set.add(555);set.add(555);set.add(888);set.add(888);set.add(777);set.add(777);System.out.println(set);}
}
HashSet
哈希值
就是一个int类型的数值,Java中每个对象都有一个哈希值。
Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值。
package com.itchinajie.d4_collection_set;
/***目标:了解一下哈希值。*Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值。* public int hashcode():返回对象的哈希值。*同一个对象多次调用hashCode方法返回的哈希值是相同的。*不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。*/
public class SetTest2 {public static void main(String[] args) {Student s1 = new Student("蜘蛛精",25,169.5);Student s2 = new Student("紫霞",24,166.5);System.out.println(s1.hashCode());System.out.println(s1.hashCode());System.out.println(s2.hashCode());String str = new String("abc");String str2 = new String("acD");System.out.println(str.hashCode());System.out.println(str2.hashCode());}
}
对象哈希值的特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的。
不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。
HashSet的底层原理
了解一下数据结构(树)
1、普通二叉树(无用)
2、二叉查找树(二叉排序树)
小的存左边, 大的存右边, 一样的不存。
3、平衡二叉树(java常用)
4、java用的红黑树
深入理解HashSet集合去重复的机制。
HashSet集合默认不能对内容一样的两个不同对象去重复!
比如内容一样的两个学生对象存入到HashSet集合中去,HashSet集合是不能去重复的!
如何让HashSet集合能够实现对内容一样的两个不同对象也能去重复???
如果希望Set集合认为2个内容一样的对象是重复的 必须重写对象的hashCode()和equals()方法。
package com.itchinajie.d4_collection_set;import java.util.Objects;public class Student implements Comparable<Student>{private String name;private int age;private double height;
//方式一:让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。@Overridepublic int compareTo(Student o) {//约定1: 如果左边的对象 大于 右边对象 请您返回正整数//约定2: 如果左边的对象 小于 右边对象 请您返回负整数//约定3: 如果左边的对象 等于 右边对象 请您返回0//按照年龄升序排序
// if (this.age > o.age) {
// return 1;
// }else if (this.age < o.age){
// return -1;
// }
// return 0;//return this.age - o.age;//升序return o.age - this.age;//降序}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public double getHeight() {return height;}public void setHeight(double height) {this.height = height;}public Student() {}public Student(String name, int age, double height) {this.name = name;this.age = age;this.height = height;}//容一样就只要两个对象内返回true@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return age == student.age && Double.compare(height, student.height) == 0 && Objects.equals(name, student.name);}//只要两个对象内容一样,返回的哈希值就是一样的。@Overridepublic int hashCode() {return Objects.hash(name, age, height);}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", height=" + height +'}';}
}package com.itchinajie.d4_collection_set;import java.util.HashSet;
import java.util.Set;public class SetTest3 {public static void main(String[] args) {Set<Student> students = new HashSet<>();Student s1 = new Student("至尊宝", 28, 169.6);Student s2 = new Student("蜘蛛精", 23, 169.6);Student s3 = new Student("蜘蛛精", 23, 169.6);System.out.println(s2.hashCode());System.out.println(s3.hashCode());Student s4 = new Student("牛魔王", 48, 169.6);students.add(s1);students.add(s2);students.add(s3);students.add(s4);System.out.println(students);}
}
LinkedHashSet
特点:有序、不重复、无索引。
底层原理
TreeSet
特点:不重复、无索引、可排序(默认升序排序,按照元素的大小,由小到大排序。
底层是基于红黑树实现的排序。
package com.itchinajie.d4_collection_set;import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;/*
* 目标:掌握TreeSet集合的使用
* */
public class SetTest4 {public static void main(String[] args) {Set<Integer> set1 = new TreeSet<>();set1.add(6);set1.add(5);set1.add(5);set1.add(7);System.out.println(set1);//方式一:让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。//方式二:通过调用TreeSet集合有参数构造器,可以设置Comparator对像(比较器对象,用于指定比较规则)。// new 一个TreeSet的比较器对象的有参构造器,生成一个比较器对象,然后重写对象的匿名内部类//day04_oop -> src -> com.itchinajie -> d5_arrays 中讲过自定义排序规则//TreeSet就近选择自己自带的比较器对象进行排序Set<Student> students = new TreeSet<>(( o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight()));students.add(new Student("蜘蛛精",23,169.7));students.add(new Student("紫霞",22,169.8));students.add(new Student("至尊宝",26,165.5));students.add(new Student("牛魔王",22,183.5));System.out.println(students);}
}
自定义排序规则
小结
注意:集合的并发修改异常
package com.itchinajie.d5_collection_exception;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class collection_exception {public static void main(String[] args) {//需求:找出集合中全部带“李”的名字,并从集合中删除。List<String> list = new ArrayList<>();list.add("王麻子");list.add("小李子");list.add("李爱花");list.add("张全蛋");list.add("晓李");list.add("李玉刚");System.out.println(list);//[王麻子,小李子,李爱花,张全蛋,晓李,李玉刚]//System.out.println("--------------------------------------");//需求:找出集合中全部带“李"的名字,并从集合中删除。
// Iterator<String>it = list.iterator();
// while (it.hasNext()){
// String name = it.next();
// if(name.contains("李")){
// list.remove(name);
// }
// }
// System.out.println(list);//报错//System.out.println("--------------------------------------");//使用for循环遍历集合并删除集合中带李字的名字//[王麻子,小李子,李爱花,张全蛋,晓李,李玉刚]
// for (int i=0;i<list.size();i++){
// String name = list.get(i);
// if(name.contains("李")){
// list.remove(name);
// }
// }
// System.out.println(list);//出bug,需要在他的基础上加个i--//System.out.println("--------------------------------------");//怎么解决呢?//使用for循环遍历集合并刷除集合中带李字的名字//[王麻子,小李子,李爱花,张全蛋,晓李,李玉刚]//i
// for (int i = 0;i<list.size();i++){
// String name = list.get(i);
// if(name.contains("李")) {
// list.remove(name);
// i--;
// }
// }
// System.out.println(list);//加个i--也可以//倒着删除也可以System.out.println("--------------------------------------");//用迭代器正常删除的方式需求:找出集合中全部带“李”的名字,并从集合中删除。Iterator<String> it = list.iterator();while (it.hasNext()) {String name = it.next();if (name.contains("李")) {//List.remove(name);//并发修改异常的错误。it.remove();//删除迭代器当前遍历到的数据,每删除一个数据后,相当于也在底层做了i--}}System.out.println(list);//System.out.println("--------------------------------------");//使用增强for循环遍历集合并删除数据,没有办法解决bug的//由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强fo循环遍历集合,又在同时删//除集合中的数据时,程序也会出现并发修改异常的错误
// for (String name : list){
// if(name.contains("")){
// list.remove(name);
// }
// }
// System.out.println(list);//System.out.println("--------------------------------------");//使用lambda表达式也不行
// list.forEach(name ->{
// if(name.contains("李")){
// list.remove(name);
// }
// });
// System.out.println(list);}
}
(本章图片均来自于黑马程序员视频)