学习内容
1.集合其实用起来没有多么的难,但是他难在他的底层原理,我们要去看他的源代码,然后还要知道他们之间的应用场景,在什么时候该用什么集合
1.什么是集合,他有什么好处
集合他和数组差不多,但是他比数组好用太多了,就比如说数组你只能制定长度,长度满了以后,你就不能再添加 了,或者说如果你再添加,你就只能再创建一个数组然后将数组复制过去(利用for循环)
好处就是,可以动态变化,然后可以有特定的方法增删改查。比较方便
2.集合的框架体系图
1.集合分为两种单列和双列集合
2.单列集合collection主要的子类有list和set
3.双列集合Map主要的子类有HasHMap,TreeMap,HashMaptable
4.这两种图非常重要要把他俩记下来
5.什么是单列,什么是多列,来段代码演示一下;
3.collection方法
主要就是这几种方法
3.1add添加
可以看到add(object e)就是可以添加任何类型
3.2remove删除指定元素
你可以删除对象,返回的就是Boolean类型,你也可以删除索引,来代码看一下
3.3contains查找元素是否存在
3.4size获得元素个数
3.5isEmpty判断是否为空
3.6clear清空
3.7addAll添加多个元素
这个addAll是可以添加一个集合的,
3.8containsAll查找多个元素是否都存在
3.9removeAll删除多个元素
4.collection遍历
4.1.迭代器遍历
iterator遍历器遍历
public class collection2 {
@SuppressWarnings({"all"})
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(new Book("红楼梦","小红",15));
list.add(new Book("三国杀","小黄",15));
list.add(new Book("西游记","小蓝",15));
list.add(new Book("爱情公寓","小绿",15));
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
}
}
class Book{
private String name;
private String o;
private int age;
public Book(String name, String o, int age) {
this.name = name;
this.o = o;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getO() {
return o;
}
public void setO(String o) {
this.o = o;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", o='" + o + '\'' +
", age=" + age +
'}';
}
}
我们来解释一下他的工作原理
创造一个iterator对象来接收list.iterator
然后在while()里面iterator.hasNext()他的作用就是来判断下一个是否还存在。
然后iterator.next是下一个对象
然后再上面那段程序里面,Book这里类的对象,编译对象还是book,意思就是iterator执行到book类的其中一个对象之后,编译的话还是按照book里面编译
他还是有快捷键的,itit就可以了
当他while循环结束之后,这个时候iterator就会指向最后一个元素,然后如果你再取iterator.next就会抛出异常
4.2增强for循环
就是上面的样子
for(元素类型 元素名:集合名/数组名){
语句
}
4.3练习题
public class test {
@SuppressWarnings({"all"})
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(new dog("小黄",10));
list.add(new dog("小王",12));
list.add(new dog("小刘",14));
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println("list+"+next);
}
for (Object dog : list){
System.out.println(dog);
}
}
}
class dog{
private String name;
private int age;
public dog() {
}
public dog(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public String toString() {
return "dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
5.List
5.1基本介绍
1.list集合他的存入和取出的顺序其实是一致的,并且是可以重复的
2.每个元素都有其对应的顺序索引,他是支持索引的
3.list的实现子类其实有很多,我们之前只是展示了其中很少的一部分
5.2list接口的常用方法
我们一个个来看一下
5.2.1add在index位置插入ele元素
5.2.2addAll
5.2.3get获得指定索引的元素
比较简单
5.2.4indexOf返回该元素第一次出现的位置
5.2.4lastindexOf返回该元素最后一次出现的位置
5.2.5remove(int index):移除指定index位置的元素,并返回此元素
5.2.6object set(int index,object ele):设置指定index位置的元素为ele,相当于是替换。
5.2.7 list sublist(int fromlndex,int tolndex):返回从fromlndex到tolndex位置的子集合
由此可见他是不包含5的也就是他是不包含toIndex
完整代码在这
public class list {
@SuppressWarnings({"all"})
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("jack");
list.add("tom");
list.add("mark");
list.add("lxy");
list.add("lxy");
list.add("jack");
//1.add
list.add(2,"小狗");
System.out.println(list);
//2.addAll
ArrayList list1 = new ArrayList();
list1.add("111");
list1.add("222");
list.add(2,list1);
System.out.println(list);
//4.indexOf
System.out.println(list.indexOf("jack"));
//5.lastindexOf
System.out.println(list.lastIndexOf("jack"));
//6.remove(int index):移除指定index位置的元素,并返回此元素
//list.remove(0);
//System.out.println(list);
//7.object set(int index,object ele):设置指定index位置的元素为ele,
//相当于是替换。
list.set(0,"jack被替换了");
System.out.println(list);
//8. list sublist(int fromlndex,int tolndex):返回从fromlndex到
//tolndex位置的子集合
System.out.println( list.subList(1,5));
}
}
5.3遍历方式
5.3.1iterator
5.3.2增强for
完整代码在这
public class list1 {
@SuppressWarnings({"all"})
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("jack");
list.add("tom");
list.add("mark");
list.add("lxy");
list.add("lxy");
list.add("jack");
//1.iterator
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
System.out.println("__________");
//2.增强for
for (Object oj : list){
System.out.println(oj);
}
}
}
5.4练习题
package List;
import java.awt.print.Book;
import java.util.ArrayList;
import java.util.Iterator;
//使用list的实现类添加三本图书,并遍历,打印如下效果
//名称: xx 价格:xx 作者:xx
//名称: xx 价格:xx 作者:xx
//名称: xx 价格:xx 作者:xx
//要求
//1)按价格排序,从低到高(使用冒泡法)
//2)要求使用arraylist,linkedlist和vector 三种集合实现
@SuppressWarnings({"all"})
public class listTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(new book("西游记","罗贯中",18));
list.add(new book("红楼梦","刘星岳",80));
list.add(new book("三国志","五岑",25));
for (Object o :list){
System.out.println(o);
}
sort(list);
System.out.println("-------");
for (Object o :list){
System.out.println(o);
}
}
//排序
public static void sort(ArrayList list){
int temp = list.size();
for (int i = 0; i < temp-1; i++) {
for (int j = 0; j < temp-1-i; j++) {
book book1 = (book) list.get(j);
book book2 = (book) list.get(j+1);
if (book1.getPrive()>book2.getPrive()){
list.set(j+1,book1);
list.set(j,book2);
}
}
}
}
}
class book{
private String name;
private String o;
private int prive;
public book(String name, String o, int prive) {
this.name = name;
this.o = o;
this.prive = prive;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getO() {
return o;
}
public void setO(String o) {
this.o = o;
}
public int getPrive() {
return prive;
}
public void setPrive(int prive) {
this.prive = prive;
}
@Override
public String toString() {
return "book{" +
"name='" + name + '\'' +
", o='" + o + '\'' +
", prive=" + prive +
'}';
}
然后我们在这里讲一下,那个冒泡排序,他的第一个循环,i其实控制的是他的循环层数,比如说你有四个数进行比较,那他循环之后就会有一个最大数在最后面,也就是他的只需要循环3次,也就是他的个数减1;
然后下面就好懂了
6ArrayList
6.1ArrayList的主要事项
1.ArrayList它里面是可以放入任何元素的,包括空null,也是可以放到里面的,而且可以放多个
2.ArrayList他的底层其实是基于数组实现的
3.ArrayList基本等同于Vector,但是ArrayList的线程是不安全的(看源码)
他是可以放多个null的
6.2ArrayList的扩容机制
刚刚我们说了,ArrayList的底层是用数组来实现的,其实在源码中他在维护一个ElementDate的一个数组
当我们创造对象的时候,如果我们使用的是无参构造器,一开始elementDate的容量其实是0,当我们添加第一个元素的时候,容量变为10,然后再次添加的话,就变为他的1.5倍,然后依次是这个样子增加
然后如果我们使用的指定大小的构造器,那么以后添加大小的话,就是*1.5这个样子
7.Vector
7.1Vector注意事项
1.和ArrayList是一样的他也是底层为elementDate数组,底层是基于数组实现的
2.Vector是线程同步的,也就是线程安全
3.在需要线程同步安全的情况下,优先考虑Vector
7.2Vector的扩容机制
他和ArrayList非常相似
当使用无参构造是,一开始他的容量是10,然后就是按两倍开始扩容
当使用有规定长度的构造器时,然后按两倍开始扩容
8.ListedList
他的底层是一个双向链表
package List;
public class Listedlist {
public static void main(String[] args) {
Node jack = new Node("jack");
Node mark = new Node("mark");
Node lxy = new Node("lxy");
jack.next = mark;
mark.next = lxy;
lxy.pre = mark;
mark.pre = jack;
Node fist = jack;
Node last = lxy;
//头到尾
while (true){
if (fist == null){
break;
}
System.out.println(fist);
fist = fist.next;
}
}
}
class Node{
public Object item;
public Node next;
public Node pre;
public Node(Object name){
this.item = name;
}
public String toString(){
return "Node name" + item;
}
}
有点难的还是
9.ArrayList和ListedList的区别
10.set
1.set他是无序的,意思就是他存放进去的数据与输出的数据不一样
2.他是不支持索引的
3.他的数据不能重复,null只能有一个
来段代码看一下
10.1遍历方式
还是那两种
1.
2.增强for循环
11.HashSet
1.HashSet实现了set的接口与方法
因为他是继承的set嘛
2.HashSet实质上是HashMap,我们可以看一下源码
3.HashSet他存入的数据与取出的顺序不一致之前在set说过了
4.不能有重复对象
11.1HashSet的底层机制
HashSet的底层其实是HashMap咱们之前也讲过,而HashSet的底层其实是数组+链表加红黑树
11.2HashSet的扩容机制
1.HashSet的底层其实是HashMap咱们之前也讲过
2.当我们在添加元素的时候,会先得到一个hash值,然后再将这个转化成索引
3.然后找的存储表table,然后如果没有则直接加入,如果有调用equals方法进行比较,如果相同则放弃添加,如果不相同,则添加到后面
4.如果链表添加到了64,则会变成红黑树
11.3执行过程
第一步
也就是个构造器,简单
第二步
add方法
第三步
执行put方法
他那个hash值其实并不是只靠hashCode得来的,还有i个移位16的操作
第四步(这一步是在HashMap的源码中的)
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//上面就是定义了辅助变量
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//这上面就是进行了扩容,怎么扩容的就是看源码,然后<<4(位移4),然后还有一个临界值
//0.75*那个容量,也即是12,当到达12时,就要准备扩容了
if ((p = tab[i = (n - 1) & hash]) == null)//当你计算出那个索引后
tab[i] = newNode(hash, key, value, null);//到这个地方
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;//然后到这里,modcount++
if (++size > threshold)//他比较的就是那个临界值
resize();
afterNodeInsertion(evict);//他其实是个空方法,是留给HashMap子类实现的
return null;//返回null就成功了
}
分析了那么长时间,才把Java加入到里面
接下来开始分析第二次add方法
当执行第二次add的时候,其实区别就是没有在创建table表
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
也就是这个,之后就是一样的了
分析第三次,相同的情况
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//上面就是定义了辅助变量
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;//让我们直接看这里把,这里定义了局部变量
//下面就分成了三种情况,
//原有元素与准备加入两个的hash值一样
//并且满足其中一个就符合
//1.k对象的与已有对象的内容相同
//2.准备加入的k与node指向的节点是同一个对象
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)//判断是否是一颗红黑树
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {//这里就判断是否是一个链表
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {//如果查找后没有一样的,就添加在后面
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//查找后有一样的就break跳出
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
练习题
@SuppressWarnings({"all"})
public class Test {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
hashSet.add(new Employee("lxy",18));
hashSet.add(new Employee("lxz",19));
hashSet.add(new Employee("lxy",18));
System.out.println(hashSet);
}
}
class Employee{
private String name;
private int age;
public Employee(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return age == employee.age && Objects.equals(name, employee.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
12.LinkedHashset
1.他是HashSet的子类
2.他的底层是一个数组+双向链表
3.他其实也是用hash值来进行存放,不过他是用链表来维持顺序,
4.他不能存放重复数据
练习题
import java.util.LinkedHashSet;
import java.util.Objects;
@SuppressWarnings({"all"})
public class test {
public static void main(String[] args) {
LinkedHashSet linkedHashSet = new LinkedHashSet();
linkedHashSet.add(new car("ixaolan",85.0));
linkedHashSet.add(new car("xiaoh",95.0));
linkedHashSet.add(new car("ixaolan",85.0));
System.out.println(linkedHashSet);
}
}
class car{
private String name;
private double price;
public car(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "car{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
car car = (car) o;
return Double.compare(price, car.price) == 0 && Objects.equals(name, car.name);
}
@Override
public int hashCode() {
return Objects.hash(name, price);
}
}
13Map
13.1常用方法
13.2遍历方法
他其实就是用上面的方法,然后利用增强for或者itorate实现
练习题
package List.MAp;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
//使用hashmap添加3个员工对象,要求
//键:员工id
//值:员工对象
//并遍历显示工资》18000的员工(遍历方式最少两种)
//员工类:姓名,工资,员工id
@SuppressWarnings({"all"})
public class test {
public static void main(String[] args) {
HashMap map = new HashMap();
map.put(12345,new player("刘星岳",15000,"12345"));
map.put(12346,new player("范玉涵",18000,"12346"));
map.put(12347,new player("李强",20000,"12347"));
Collection values = map.values();
Iterator iterator = values.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
player player = (player) next;
if(((player) next).getPrice()>=18000){
System.out.println(next);
}
}
}
}
class player{
private String name;
private int price;
private String id;
public player() {
}
public player(String name, int price, String id) {
this.name = name;
this.price = price;
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "player{" +
"name='" + name + '\'' +
", price=" + price +
", id='" + id + '\'' +
'}';
}
14.HashMap的底层
这个地方和HashSet其实是一样的上面讲过了
可以自己debug看一下
15.HashTable
16.Properties
17.选择集合
18.作业
@SuppressWarnings({"all"})
public class Test1 {
public static void main(String[] args) {
news news1 = new news("新闻一","新冠确诊病例超千万,数百万印度教信徒赴恒河圣浴引民众担忧");
news news2 = new news("新闻二","男子突然想起2个月前钓的鱼还在网兜里,捞起一看赶紧放生");
//(3)将新闻对象添加到arraylist集合中,并且进行倒序遍历;
ArrayList arraylist = new ArrayList();
arraylist.add(news1);
arraylist.add(news2);
Iterator iterator = arraylist.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
news news = (news) next;
int temp = news.getSome().length();
if (temp>=15){
for (int i = 0; i <= 15; i++) {
char c = news.getSome().charAt(i);
System.out.print(c);
}
}
System.out.println(" ");
}
}
}
//(1)封装一个新闻类,包含标题和内容属性,提 提供get,set方法,重写tostring方法,打印对象
//时只打印标题;
class news{
private String name;
private String some;
public news(String name, String some) {
this.name = name;
this.some = some;
}
public news() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSome() {
return some;
}
public void setSome(String some) {
this.some = some;
}
@Override
public String toString() {
return "news{" +
"name='" + name + '\'' +
'}';
}
}
public class Test2 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
ArrayList arrayList1 = new ArrayList();
car car1 = new car("宝马",40000);
car car2 = new car("宾利",5000000);
//1.add:添加单个元素
arrayList.add(car1);
arrayList.add(car2);
// 2.remove:删除指定元素 car car new car("宝马",40000);
arrayList.remove(car2);
// 3.contains:查找元素是否存在 car car2new car("宾利",5000000);
System.out.println(arrayList.contains(car1));
// 4.size:获取元素个数
System.out.println(arrayList.size());
// 5.isempty:判断是否为空
System.out.println(arrayList.isEmpty());
6.clear:清空
arrayList.clear();
7.addall:添加多个元素
arrayList1.add("111");
arrayList1.add("111");
arrayList1.add("111");
arrayList1.add("111");
arrayList.addAll(arrayList1);
System.out.println(arrayList);
8.containsail:查找多个元素是否都存在
System.out.println(arrayList.containsAll(arrayList1));
9.removeall:删除多个元素
arrayList.removeAll(arrayList);
System.out.println(arrayList);
使用增强for和迭代器来遍历所有的car,需要重写 car 的tostring方法
}
}
class car{
private String name;
private int price;
public car() {
}
public car(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "car{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
public class Test3 {
public static void main(String[] args) {
//1)使用hashmap类实例化一个map类型的对象m,键(string)和值(int)分别用于存储员
HashMap map = new HashMap();
map.put("jack",650);
map.put("tom",1200);
map.put("smith",2900);
map.put("jack",2600);
//2)将jack的工资更改为2600元
System.out.println(map);
//3) 为所有员工工资加薪100元;
Set keySet = map.keySet();
for (Object key:keySet){
map.put(key,(Integer)map.get(key)+100);
}
//4)遍历集合中所有的工资
Collection values = map.values();
Iterator iterator = values.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//5 )遍历集合中所有的员工
for (Object obj : keySet){
System.out.println(obj);
}
}
}
1.先说一下啊HashSet是怎么去重的吧,咱们前面也说了,他是一个HashCode+equals的一个结果,他的底层是先通过算法得到一个Hash值,然后在通过Hash值得到对应的索引,如过该索引上没有数据,则可以直接存放,若有数据,则就进行equals比较(里面的遍历比较),如果比较后,不相同就加入,如果相同,就不加入
其实前五行,没有什么问题
到了下一行,p1.name就是把里面的AA换成的CC但是你要知道他为什么存放在那里,是因为他的hash值,你要把AA换成CC了,hash值不会变
然后你删除p1,现在你注意看,他要删除p1,也是要先计算Hsah值,不过现在他计算的hash值是根据1001和CC计算的,所以p1那个索引不会被删除
然后你打印其实还是两个对象
然后你存一个对象1001和CC,索引会根据他俩计算,不会对p1造成影响
然后你打印就是三个对象
你又存1001和AA这个索引就会和p1冲突,然后挂在后面,所以有4个对象