1、集合的理解好处
- 数组
- 长度开始必须指定,而且一旦指定,不能更改
- 保存的必须为同一类型的元素
- 使用数组进行增加/删除元素比较麻烦
- 集合
- 可以动态保存任意多个对象,使用比较方便
- 提供了一系列方便的操作对象的方法:add、remove、set、get等
- 使用集合添加,删除新元素简洁了
2、集合框架体系图
- Collection(单列集合:List、Set)
- Map(双列集合:k、v键值对)
2.1、Collection接口和常用方法
- Collection接口实现类的特点
public interface Collection<E> extends Iterable<E>
- Collection实现子类可以存放多个元素,每个元素可以是Object
- 有些Collection的实现类,可以存放重复的元素
- 有些Collection的实现类,有些是有序的(List),有些是无序的(Set)
- Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的
- Collection接口遍历元素方式-使用Iterator(迭代器)
- Collection接口和常用方法(以实现类ArrayList为例)
- add:添加单个元素
- remove:删除指定元素
- contains:查找元素是否存在
- size:获取元素的个数
- isEmpty:判断是否为空
- clear:清空
- addAll:添加多个元素
- containsAll:查找多个元素是否都存在
- removeAll:删除多个元素
List list = new ArrayList();
//add:添加单个元素
list.add("jack");
list.add(10);
list.add(true);
//remove:删除指定元素
list.remove("jack");//指定删除某个元素
list.remove(0);//删除索引第一个元素
//contains:查找元素是否存在
list.contains("jack");
//addAll:添加多个元素
List list1 = new ArrayList();
list1.add("qw");
list1.add("qq");
list.addAll(list1);
- 用迭代器和增强for循环两种方式来遍历
public class Collection练习 {
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Dog("小黑",3));
//增强for
for (Object dog:list) {
System.out.println(dog);
}
//使用迭代器
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object dog = iterator.next();
System.out.println(dog);
}
}
}
class Dog{
private String name;
private int age;
2.2、List接口和常用方法
- List接口的基本介绍
- List接口是Collection接口的子接口
- Lsit集合类中元素有序(即添加顺序和取出顺序一致),且可重复
- List集合中每个元素都有其对应的顺序索引,即支持索引
- List接口常用方法
List list = new ArrayList();
list.add("小明");
list.add("小黑");
//add(int index) :在index的位置插入一个对象
list.add(1,"小红");
System.out.println(list); //[小明, 小红, 小黑]
//addAll(int index ,Collection con) :从index位置开始将con中的元素添加进来
List list2 = new ArrayList();
list2.add("jack");
list2.add("tom");
list.addAll(1,list2);
System.out.println(list); //[小明, jack, tom, 小红, 小黑]
//get ;获取指定index位置的元素
//indexOf(Object obj) :返回obj在集合中首次出现的位置
System.out.println(list.indexOf("tom")); //2
//lastIndexOf(Object obj):返回obj在集合中末次出现的位置
list.add("小明");
System.out.println(list); //[小明, jack, tom, 小红, 小黑, 小明]
System.out.println(list.lastIndexOf("小明")); //5
//remove(int index):移除指定index位置的元素,并返回此元素
System.out.println(list.remove(5)); //小明
//set(int index,Object obj):设置指定index位置元素为obj,相当于是替换或者修改
list.set(2,"玛丽");
System.out.println(list); //[小明, jack, 玛丽, 小红, 小黑]
//subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
List list1 = list.subList(0, 3);
System.out.println(list1); //[小明, jack, 玛丽]
}
- List接口练习1
- 添加几个元素,在2号位插入一个元素,获得第五个元素,删除第六个元素,修改第七个元素,再使用迭代器遍历集合
- 要求,使用List的实现类ArrayList完成
public class List练习 {
public static void main(String[] args) {
List list = new ArrayList();
for (int i = 0; i < 12; i++) {
list.add("hello"+i);
}
list.add(1,"你是谁");
list.get(4);
list.remove(5);
list.set(6,"修改");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println(obj);
}
}
}
- List接口练习2
- 使用List的实现类添加三本书,并遍历
- 要求:
- 按价格排序,从低到高(使用冒泡法)
- 要求使用ArrayList、LinkedList和Vector三种集合实现
public class List练习2 {
public static void main(String[] args) {
List list = new ArrayList();
//List list1 = new Vector();
//List list2 = new LinkedList();
list.add(new Book("红楼梦","曹雪芹",100));
list.add(new Book("红楼","曹雪",10));
list.add(new Book("红","曹",10));
for (Object o :list) {
System.out.println(o);
}
sort(list);
for (Object o :list) {
System.out.println(o);
}
}
public static void sort(List list){
for (int i = 0; i < list.size()-1; i++) {
for (int j = 0; j < list.size()-1-i; j++) {
Book book1 = (Book) list.get(j);
Book book2 = (Book) list.get(j+1);
if (book1.getPrice()> book2.getPrice()){
list.set(j,book2);
list.set(j+1,book1);
}
}
}
}
}
class Book{
private String name;
private String author;
private double price;
2.2.1、ArrayList
- ArrayList 可以加入null,并且可以加入多个
- ArrayList是由数组(可变数组)实现数据存储的
- ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高),在多线程情况下,不建议使用ArrayList
- ArrayList增删效率低,查改的效率高
ArrayList arrayList = new ArrayList();
arrayList.add(null);
arrayList.add("jack");
arrayList.add(null);
System.out.println(arrayList); //[null, jack, null]
- 当创建ArrayList对象时,如果使用的是无参构造,则初始数组容量为0,第1次添加,则扩容数组为10,如果需要再次扩容,则扩容数组的1.5倍
- 如果使用的是指定大小的构造器,则初始数组容量为指定大小,如果需要扩容,则直接扩容数组的1.5倍
2.2.2、Vector
- Vector底层也是一个对象数组
- Vector是由数组(可变数组)实现数据存储的
- Vector是线程同步的,即线程安全(执行效率低),Vector类的操作方法带有synchronized
- 在开发中,需要线程同步安全时,考虑使用Vector
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
- 当创建Vector对象时,如果使用的是无参构造,则默认数组容量为10,满后,就按照2倍扩容
- 如果使用的是指定大小的构造器,则初始数组容量为指定大小,如果需要扩容,则直接按2倍扩容
2.2.3、LinkedList
- LinkedList实现了双向链表和双端队列特点
- 可以添加任意元素(元素可以重复),包括null
- 线程不安全,没有实现同步
- LinkedList底层维护了一个双向链表
- LinkedList中维护了两个属性first和last分别指向首节点和为节点
- 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表
- 所有LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高
- LinkedList增删效率高,查改的效率低
package com.han;
public class LinkedList {
public static void main(String[] args) {
//模拟一个简单的双向链表
Node jack = new Node("jack");
Node tom = new Node("tom");
Node make = new Node("make");
//连接三个节点,形成双向链表
//jack->tom->make
jack.next=tom;
tom.next=make;
//make->tom->jack
make.pre=tom;
tom.pre=jack;
Node first=jack;//头节点
Node last=make;//尾节点
//从头到尾的遍历
while (true){
if (first==null){
break;
}
System.out.println(first); //jack/ntom/nmake
first=first.next;
}
//从尾到头的遍历
while (true){
if (last==null){
break;
}
System.out.println(last); //make/ntom/njack
last=last.pre;
}
//链表添加一个数据
//在tom和make之间插入一个smith
Node smith = new Node("smith");
smith.pre=tom;
smith.next=make;
tom.next=smith;
make.pre=smith;
first=jack;
while (true){
if (first==null){
break;
}
System.out.println(first); //jack/ntom/nsmith/nmake
first=first.next;
}
}
}
//定义一个Node类,Node对象,表示双向链表的一个节点
class Node{
public Object item; //真正存放数据
public Node next; //指向后一个节点
public Node pre; //指向前一个节点
public Node(Object name) {
this.item = name;
}
@Override
public String toString() {
return "Node name=" +item;
}
}
2.3、Set接口和常用方法
- 无序,没有索引
- 不允许重复元素,所以最多包含一个null
- 和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样
- Set接口的遍历方式:1.可以使用迭代器 2. 增强for 3. 不能使用索引的方式来获取
- Set接口对象,不能通过索引来获取,即不能通过普通for循环遍历
Set set = new HashSet();
set.add("join");
set.add("lusy");
set.add("jack");
set.add("join");//再次添加相同元素
set.add(null);
set.add(null);//再次添加null
System.out.println(set); //[null, join, lusy, jack]
//遍历方式1:迭代器
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.print(obj+" "); //null join lusy jack
}
//遍历方式2:增强for
for (Object o :set) {
System.out.print(o+" "); //null join lusy jack
}
2.3.1、HashSet(数组+单向链表)
- HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树)
set=new HashSet();
System.out.println(set); //[]
set.add("lusy");//添加成功
set.add("lusy");//加入不了
set.add(new Dog1("tom"));//ok
set.add(new Dog1("tom"));//ok
System.out.println(set); //[com.han.Dog1@4554617c, com.han.Dog1@1b6d3586, lusy]
//底层机制
set.add(new String("hsp"));//ok
set.add(new String("hsp"));//加入不了
System.out.println(set); //[hsp, com.han.Dog1@4554617c, com.han.Dog1@1b6d3586, lusy]
}
}
class Dog1{
private String name;
2.3.2、LinkedHashSet
- LinkedHashSet是HashSet的子类
- LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表
- LinkedHashSet根据元素的hashCode值来决定元素的存储位置
- LinkedHashSet不允许添加重复元素
3、Map接口和常用方法
- Map与Collection并列存在,用于保存具有映射关系的数据:Key-Value
- Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
- Map中的key不允许重复
- Map中的value可以重复
- Map的key可以为null,value也可以为null,注意 key为null,只能有一个,value为null,可以有多个
- 常用String类作为Map的key
- key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
- Map接口的常用方法
- put:添加
- remove:根据键删除映射关系
- get:根据键获取值
- size:获取元素个数
- isEmpty:判断 个数是否为0
- clear:清除
- containsKey:查找键是否存在
Map map1 = new HashMap();
map1.put("q","qq");
map1.put("a",null);
map1.put(null,"b");
System.out.println(map1); //{null=b, q=qq, a=null}
System.out.println(map1.remove(null));//b
System.out.println(map1.get("a"));//null
System.out.println(map1.size());//2
System.out.println(map1.isEmpty());//false
System.out.println(map1.containsKey("q"));//true
- Map接口的遍历
//第一组:取出所有的key,通过key取出对应的Value
Set keySet = map1.keySet();
//(1)增强for
for (Object key :keySet) {
System.out.print(key+"-"+map1.get(key)+" "); //q-qq a-null
}
//(2)迭代器
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.print(key+"-"+map1.get(key)+" "); //q-qq a-null
}
//第二组:把所有的value值取出
Collection values = map1.values();
//1.增强for
for (Object value :values) {
System.out.println(value); //qq null
}
//2.迭代器
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()) {
Object value = iterator1.next();
System.out.println(value); //qq null
}
//第三组:通过EntrySet 来获取 k-v
Set entrySet = map1.entrySet();
//1.增强for
for (Object entry :entrySet) {
Map.Entry m =(Map.Entry) entry;
System.out.println(m.getKey()+"-"+m.getValue()); //q-qq a-null
}
//2.迭代器
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()) {
Object next = iterator2.next();
System.out.println(next); //q=qq a=null
}
3.1、HashMap
- Map接口的常用实现类:HashMap、Hashtable、Properties
- HashMap是Map接口使用频率最高的实现类
- HashMap是以key-value对的方式来存储数据的
- key不能重复,但是值可以重复,允许使用null键和null值
- 如果添加相同的key,则会覆盖原来的key-value,等同于修改(key不会替换,value会替换)
- 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的(数组+链表+红黑树)
- HashMap没有实现同步,因此是线程不安全的
扩容机制:
3.2、HashTable
- 存放的元素是键值对:k-v
- HashTable的键 和值都不能为null,否则会抛出NullPointerException
- HashTable使用方法基本和HashMap一样
- HashTable是线程安全的,HashMap是线程不安全的
3.3、Map接口实现类-Properties
- Properties类继承自HashTable类并且实现了Map接口,也是使用一种键值对形式来保存数据
- 它的使用特点和HashTable类似
- 它的键 和值都不能为null
- Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改
- 工作后:xxx.properties文件通常作为配置文件
如何选择集合实现类
4、编程题
- 第一题:
public class homework01 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add(new News("新冠肺炎病例超千万"));
arrayList.add(new News("男子突然想起两个月前钓的鱼还在网兜里"));
for (int i = arrayList.size()-1; i >=0; i--) {
//System.out.println(arrayList.get(i));
News news =(News) arrayList.get(i);
System.out.println(processTitle(news.getTitle()));
}
}
public static String processTitle(String title){
if (title==null){
return "";
}
if (title.length()>15){
return title.substring(0,15)+"...";
}else {
return title;
}
}
}
class News{
private String title;
private String content;
public News(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "News{" +
"title='" + title + '\'' +
'}';
}
}
- 第二题:
Map map = new HashMap();
map.put("jack",650); //int->Integer
map.put("tom",1200); //int->Integer
map.put("smith",2900); //int->Integer
map.put("jack",2600);//替换
Set keySet = map.keySet();
for (Object key :keySet) {
map.put(key,(Integer)map.get(key)+100);
}
System.out.println(map);
//遍历
Set entrySet = map.entrySet();
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()) {
Map.Entry entry =(Map.Entry) iterator.next();
System.out.println(entry.getKey()+"-"+entry.getValue());
}
//遍历工资
Collection values = map.values();
for (Object value :values) {
System.out.println(value);
}