目录
集合的理解和好处
1、可以保存任意多个对象
2、提供了一系列方便的操作对象的方法:add 、remove 、set、get等
集合主要是两组<单列集合、双列集合>
collection接口有两个重要的子接口 list set 他们的实现子类都是单列集合
Map接口的实现子类 是双列集合
collection接口继承关系图:
Map接口继承关系图:
Collection接口和常用方法
Collection接口实现类的特点
1、Collection实现子类可以存放多个元素、每个元素可以是Object
2、有些Collection的实现类、可以存放多个重复的元素,有的不可以
3、有些Collection的实现类、有些是有序的(list)有些不是有序的(set)
4、Collection接口没有直接的实现子类、是通过它的子接口set和list来实现的
Collection接口的常用方法
add添加单个元素【o.add(XXX)】
remove 删除指定元素
contains查找元素是否存在
size获取元素个数
isEmpty判断是否为空
clear清空
assAll添加多个元素
containsAll查找对个元素是否存在
removeAll删除多个元素
Collection接口遍历元素的方式
1、使用iterator(迭代器)
1、Iterator对象称作迭代器,主要用于遍历Collection集合中的元素
2、所有实现了Collection接口的集合类都有一个Iterator()方法,用于返回一个实现了Iterator接口的对象即返回一个迭代器
3、Iteraror仅适用于集合的遍历
迭代器的执行原理
迭代器使用
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
// write your code here
//创建集合对象
Collection col = new ArrayList();
//使用集合中的add方法将每一本书加起来
col.add(new Book("西游记","罗贯中",30));
col.add(new Book("红楼梦","曹雪芹",23));
col.add(new Book("线性代数","数学教授",32));
System.out.println(col);//全部一次性输出
//下面使用迭代器遍历输出
//快捷键输出迭代器循环 itit或者 ctrl+j
Iterator iterator = col.iterator();//先将游标初始化
while (iterator.hasNext()) {//循环条件是集合的下一个元素不是空(要先调用,下面注意中查看)
Object book = iterator.next();//这时将游标指向要拿出的数据,并将它赋给book
System.out.println("书本:"+book);
}
System.out.println("第二次使用迭代器遍历的集合");
Iterator iterator2 = col.iterator();//在第二次遍历的话 “游标需要初始化”
while(iterator2.hasNext()){
Object book2 = iterator2.next();
System.out.println("book2"+book2);
}
}
}
class Book{
private String BookName;
private String author;
private double price;
public Book(String bookName, String author, double price) {
BookName = bookName;
this.author = author;
this.price = price;
}
public String getBookName() {
return BookName;
}
public void setBookName(String bookName) {
BookName = bookName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"BookName='" + BookName + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
}
class add{
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
for (int i = 0; i < 10; i++) {
arrayList.add(i);
}
}
}
注意:使用迭代器遍历是,调用i.next()方法之前必须调用i.hasNext()方法进行检测下一个集合空间是否为空
若不调用,则下一条记录无效,直接调用i.next ()会抛出NoSuchElementExceptio异常
2、使用增强for循环
语法:
for(元素基本类型 元素名 : 集合或者数组名){
//逻辑语句
}
list接口和常用方法
list接口基本介绍
1、list集合类中元素有序(即添加顺序和取出顺序一样)且可重复
2、list集合中的每个元素都有其对应的顺序索引、即支持索引
索引是从零开始的
3、list容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
常用方法
1、add添加元素
2、add(int i,object o):在索引为i的地方添加元素o
3、Boolean addAll(int i ,Collection c):从i位置开始将c中的所有元素添加进来
4、int indexOf(Object o)返回在集合中元素o首次出现的位置
5、int lastIndexOf(Object o)返回在集合中元素o最后一次出现的位置
6、remove(int i)移除指定位置的元素
7、set(int i,Object o)将位置 i 的元素替换为o
8、List subList(int i,int j)返回[i,j)位置的子集合
Arraylist
的注意事项
1 permits all elements,including null ,ArraysList可以加入null,并且可以加入多个
2、Arraylist是由数组来实现数据的储存的
3、ArrayList基本等同于Vector,除了ArrayList是线程不安全的(执行效率高)在多线程情况下。不建议使用ArrayList
底层结构和源码分析
!!自己调试源码!!
1、ArrayList中维护了一个Object类型的数组elementDate
2、当穿创建ArrayList对象时,如果使用的是无参构造器,则初始化elementDate容量为0,第一次添加,则扩容elementDate为10,如果需要再次扩容,则扩容elementDate为1.5倍
3、如果使用的是指定大小的构造器,则初始化elementDate容量为指定大小,如果需要扩容,则直接扩容elementDate为1.5倍
vector底层结构和源码剖析
vector 基本介绍(自己deBug查看源码运行)
1、Vector底层也是一个对象数组,protected Object [ ] elementDate ;
2、Vector是线程同步的,即线程安全,Vector类的操作方法带有上synchronized
3、在开发中,需要线程同步安全时,考虑使用Vector
LinkedList的底层操作机制
1、linkedList底层维护了一个双向链表。
2、LinkedList中维护了两个属性 first和last分别指向首节点和尾节点
3、每个节点(Node对象),里面又维护了prev,next。item三个属性,其中通过prev指向前一个。通过next指向后一个节点,最终实现双向链表。
4、所以linkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。
模拟一个双向链表
最简单双向链表代码
public class LinkedShow_ {
public static void main(String[] args) {
//创建链表的单个节点
Node jack = new Node("jack");
Node tom = new Node("tom");
Node join = new Node("join");
//将单个节点连接起来
jack.next = tom;
tom.prev = jack;
tom.next = join;
join.prev = tom;
//确定头结点和尾节点
Node first = jack;
Node last = join;
//循环输出各个节点的元素
while (true) {
//从头结点开始输出
if (first == null) {
break;
}
System.out.println(first);
first = first.next;//每次将头结点的指向 向后移动
}
}
}
class Node {
Object item;
Node next;
Node prev;
public Node(Object item) {
this.item = item;
}
@Override
public String toString() {
return "Node name = " + item;
}
}
set接口和常用方法
Set接口基本介绍
1、无序(添加和取出的顺序不一样),没有索引
2、不允许重复元素,所以最多包含一个null
Set接口的常用方法
1、和List接口一样,Set接口也是Collection接口的子接口,因此,常用方法和colliction接口一样
Set的遍历方式
同Collection的遍历方式一样,因为Set接口是Collection接口的子接口
1、可使用迭代器
2、增强for
3、不能使用索引的方式来获取
set接口的实现类的对象(Set接口对象)。不能存放重复的元素,可以添加一个null
set接口对象存放数据时无序的(即添加的顺序和取出的顺序不一定相同)
取出的顺序的顺序虽然不是添加的顺序但是他是固定的
Set接口实现类—HashSet
HashSet实现了set接口,
hashset实际上是hashmap
可以存放null值,只能有一个
hashset不保证元素是有序的,取决于hash确定的索引
不能有重复的元素/对象,(和set接口一样)
HashSet练习
package HashSet_Exercise;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Objects;
@SuppressWarnings({"all"})
public class Main {
public static void main(String[] args) {
HashSet LinkedHashSet = new LinkedHashSet();
LinkedHashSet.add("jflk");
LinkedHashSet.add("fj");
LinkedHashSet.add("jflk");
HashSet hashSet = new HashSet();
hashSet.add(new Employee("jan", 10,
new Employee.MyDate(2002, 3, 4)));
hashSet.add(new Employee("jl", 20,
new Employee.MyDate(2001, 5, 3)));
hashSet.add(new Employee("jan", 10,
new Employee.MyDate(2002, 3, 4)));
System.out.println("hashSet=" + hashSet);
}
}
class Employee {
private String name;
private int age;
//静态内部类
//定义MyDate类
static class MyDate {
int year;
int month;
int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
private MyDate birthday;
public Employee(String name, int age, MyDate birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
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 + '\'' +
", brithday=" + birthday.year + "," + birthday.month + "," + birthday.day +
'}';
}
//重写equals方法,以判断生日是否相同
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(name, employee.name)
&& Objects.equals(birthday.year, employee.birthday.year)
&& Objects.equals(birthday.month, employee.birthday.month)
&& Objects.equals(birthday.day, employee.birthday.day);
}
//重写计算hash值方法
@Override
public int hashCode() {
return Objects.hash(name, birthday.year, birthday.month, birthday.day);
}
}
Set接口实现类—LinkedHashSet
LinkedHashSet是HashSet的子类
LInkedHashSet底层是一个LInkedHashMap,底层维护了一个 数字+双向链表
LInkedHashSet根据元素的HashCode值来决定元素的存储位置。同时使用链表为何要元素的次序,知识的元素看起来是以插入顺序保存的
LinkedHashSet不允许添加重复元素
自己调试LInkedHashSet源码查看!!!
Map接口和常用方法[JDK8]
- Map和Collection并列存在,用于保存具有映射关系的数据【Key—value】
- Map中的Key和Value可以是任何引用类型的数据,会封装发到HashMap$Node对象中
- Map中的Key不允许重复,原因和HashSet一样,
- Map中的Value可以重复,
- Map的Key可以为null,value也可以为null,注意key为null的情况只能有一个,value为null的情况可以有多个
- 常用的String类作为Map的key
- Key和value之间存在单向的一对一关系
k-v最后是 HashMap N o d e n o d e = n e w N o d e ( h a s h , k e y , v a l u e , n u l l ) k − v 为了方便程序员的遍历,还会创建 E n t r y S e t 集合,该集合存放的元素类型 E n t r y ,而一个 E n t r y 对象就有 k , v E n t r y S e t < E n t r y < k , v > > 即: t r a n s i e n t S e t < M a p . E n t r y < K , V > > e n t r y S e t e n t r y S e t 中,定义的类型是 M a p . E n t r y ,但实际上存放的还是 H a s h M a p Node node = newNode(hash,key,value,null) k-v为了方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素类型 Entry ,而一个Entry对象就有k,v EntrySet<Entry<k,v> > 即:transient Set<Map.Entry< K ,V > > entrySet entrySet 中,定义的类型是 Map.Entry,但实际上存放的还是 HashMap Nodenode=newNode(hash,key,value,null)k−v为了方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素类型Entry,而一个Entry对象就有k,vEntrySet<Entry<k,v>>即:transientSet<Map.Entry<K,V>>entrySetentrySet中,定义的类型是Map.Entry,但实际上存放的还是HashMapNode这是因为 static class Node < K,V> implements Map.Entry。
当把HashMap$Node对象 存放到entrySet就方便我们遍历
HashMap的六种遍历方式
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* 练习
* 使用HashMap添加的三个员工对象
* 要求:建:员工ID 值:员工对象
* 并且遍历显示工资大于18000的员工
* 员工类:姓名,工资,员工id
*/
public class HashMapExercise_ {
public static void main(String[] args) {
Map map = new HashMap();
map.put("123", new Worker("123", "小王", 34000));
map.put("456", new Worker("456", "小李", 1000));
map.put("789", new Worker("789", "jack", 80000));
System.out.println("----使用增强for循环-----");
Set keySet = map.keySet();
for (Object key : keySet) {
Worker worker = (Worker) map.get(key);
if(worker.getSalary()>800){
System.out.println(worker);
}
}
System.out.println("----使用 EntrySet迭代器遍历----");
Set entrySet = map.entrySet();
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
Map.Entry entry = (Map.Entry) next;
Worker worker = (Worker) entry.getValue();
if (worker.getSalary() > 800)
System.out.println(entry.getKey() + "_" + entry.getValue());
}
}
}
class Worker {
private String id;
private String name;
private double salary;
public Worker(String id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Worker{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", salary=" + salary +
'}';
}
}
Map接口常用方法
put 添加
remove 根据键删除映射关系
get 根据键获取值
size:获取元素个数
isEmpty:判断个数是否为零
clear:清除
containsKey:查找键是否存在
keySet:获取所有的键
entrySet:获取所有关系 k-v
values :获取所有的值
HashMap小结
- Map接口的常用实现类:HashMap、Hashtable和propreties
- HashMap是Map接口的使用频率最高的实现类
- HashMap是以 key_value 对的方式来存储数据的(HashMap$Node类型)
- key不能重复,但是值可以重复,允许使用null键和null值
- 如果添加的相同的key,则会覆盖原来的key_value。等同于修改value(key不会被替换,value会被替换)
- 与HashSet一样,不保证映射的顺序,因为底层还是以值的hash值来决定在table表中的位置
- HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized关键字
HashMap底层机制及源码剖析
HashTable的基本介绍
1、存放的元素是键值对【即K—V】
2、Hashtable的键和值都不能为null
3、hashTable使用方法基本上和HashMap一样
4、HashTable是线程安全的
简单说一下HashTable的底层
HashMap和HashTable的对比
Map接口实现类 Properties
1、Properties类继承自Hashtable类并且实现了Map接口,也是一种键值对的形式来保存数据的
2、它的使用特点和Hashtable类似
3、Properties还可以用于xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改
在开发中如何选择集合实现类
-
先判断存储的类型,(一组对象【单列】或一组键值对【双列】)
-
一组对象【单列】:Collection接口
允许重复:List 增删多:LinkedList【底层维护了一个双向链表】 改查多:ArrayList【底层维护了 Object 类型的可变数组】 不允许重复:Set 无序:HashSet【底层是HashMap。维护了一个哈希表,即【数组+链表+红黑树】】 排序:TreeSet 插入和取出顺序一致:LinkedHashSet ,维护了数组+双向链表
-
一组键值对【双列】:Map
键无序:HashMap【底层是Hash表 jdk7 :数组+链表 jdk8:数组+链表+红黑树】
建排序:TreeMap
插入和读取顺序一致:LinkedHashMap
读取文件:Properties
Collection工具类
-
Collection是一个操作Map,Set,List等集合的工具类
-
Collection中提供了一系列静态方法对集合元素进行排序。查询和修改等操作
-
排序操作
-
reverse(List):翻转List中元素的顺序
-
shuffle(List):对List中元素进行随机排序
-
sort(List):根据元素的自然顺序对指定的List集合按升序排序
-
sort(List Comparator)根据指定的Comaratpr产生的顺序对List集合元素进行排序
-
swap(List,int i,int j):将指定List集合中的i处元素和j处元素及进行交换
查找 替换 -
Object max(collection):根据元素的自然排序,返回给定集合中的最大元素
-
Object max(collection ,Comparator)根据Comparator指定的顺序返回给定集合中的最大元素
-
Object min(collection)
-
Object min(collection ,Comparator)
-
int frequency(Collection,Object):返回指定集合中指定元素出现的次数
-
void copy(List dest,List src):将src中的内容复制到dest中
-
boolean replaceAll(LIst list,Object oldVal,Object newVal):使用新值替换旧值
TreeSet分析``
public class TreeSet_ {
public static void main(String[] args) {
//进行数据的自定义有序输出
TreeSet treeSet = new TreeSet(new Comparator() {
//匿名内部类以参数形式传入TreeSet的构造其中
/*
* public TreeSet(Comparator<? super E> comparator) {//这里传入的属性必须实现Collection接口
否则会抛出类型转换异常
this(new TreeMap<>(comparator));
*
底层其实是将传入的 “比较器”对象赋给了TreeMap的属性 comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
}*/
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).compareTo((String) o2);
}
});
/*
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check 类型(可能为空)检查
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
* Comparator<? super K> cpr = comparator;//将匿名内部类对象赋值给cpr
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);//动态绑定机制,调用匿名内部类中的比较方式
if (cmp < 0)//根据返回的值,确定左右指针的指向
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;//这里会将add方法传入的属性进行强制类型转换
这就是上面说的为什么要必须实现Collection接口(add方法传入的元素必须实现Collection接口)
do {
parent = t;
cmp = k.compareTo(t.key);//进入compareTo方法进行对比确定是否出现相同K的元素
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);compareTo方法返回值是零的话说明存在K值相同元素,进行替换
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)//根据返回值确定指针指向属性的值
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}*/
treeSet.add("bio");
treeSet.add("bio");
treeSet.add("akfgdfh");
treeSet.add("lljo");
//根据匿名内部类中的比较方式,以及链表指针的连接顺序进行输出
System.out.println("treeSet="+treeSet);
}
}