Java 集合基础
1.集合基础
1.1集合概述
编程的时候如果要存储多个数据,使用长度固定的数组存储格式,不一定满足我们的需求,更适应不了变化的需求,要是想要长度可以变化的存储格式,数组就不行了,这个时候就需要集合
集合类的特点:提供一种存储空间可变的存储模型,存储的数据容量可以发生改变
集合:
- Collection接口(单列集合)
- List接口(元素内容可重复)
- ArrayList(实现类)
- LinkedList(实现类)
- …
- Set接口(元素内容不可重复)
- HashSet(实现类)
- TreeSet(实现类)
- …
- List接口(元素内容可重复)
- **Map接口 ** (双列集合)
- HashMap(实现类)
- …
2. ArrayList
2.1 ArrayList 概述
在java.util包下
- 可调整大小的数组实现
- :是一种特殊的数据类型,泛型
**用法:**在出现E的地方我们使用引用数据类型替换即可
举例:ArrayList , ArrayList
2.2 ArrayList构造方法和添加方法
方法名 | 说明 |
---|---|
public ArrayList( ) | 创建一个空的集合对象 |
public boolean add(E e) | 将指定元素追加到这个集合末尾 |
public void add(int index , E element) | 在此集合的指定位置插入指定的元素 |
import java.util.ArrayList;
public class test {
public static void main(String[] args) {
//public ArrayList( ) 创建一个空的集合对
//ArrayList<String> array = new ArrayList<>();//jdk7之后,后面的<>可以不写内容,
ArrayList<String> array = new ArrayList<String>();
System.out.println("array:"+array);//输出:array:[]
//public boolean add(E e)将指定元素追加到这个集合末尾,添加成功返回boolean值
array.add("hello");
array.add("java");
array.add("best");
System.out.println("array:"+array);//输出:array:[hello, java, best]
//public void add(int index , E element)在此集合的指定位置插入指定的元素,指定位置的元素都往后移
array.add(1,"javase");
//array.add(7,"javaee");//IndexOutOfBoundsException
System.out.println("array:"+array);//输出:array:[hello, javase, java, best]
}
}
2.3 ArrayList集合的常用方法
方法名 | 说明 |
---|---|
public boolean remove(Object o) | 删除指定元素,返回删除是否成功 |
public E remove (int index) | 删除指定索引处的元素,返回被删除的元素 |
public E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
public E get(int index) | 返回指定索引处的元素 |
public int size() | 返回后集合中的元素个数 |
import java.util.ArrayList;
public class test {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
//添加
array.add("java");
array.add("javase");
array.add("javaee");
array.add("hello");
System.out.println(array);
//public boolean remove(Object o) 删除指定元素,返回删除是否成功
System.out.println(array.remove("hello"));//true
System.out.println(array); //[java, javase, javaee]
//public E remove (int index) 删除指定索引处的元素,返回被删除的元素
System.out.println(array.remove(1));//javase
//System.out.prtinln(9);//IndexOutOfBoundsException
System.out.println(array);//[java, javaee]
//public E set(int index,E element)修改指定索引处的元素,返回被修改的元素
System.out.println(array.set(1,"lllll"));//javaee
System.out.println(array);//[java, lllll]
//public E get(int index)返回指定索引处的元素
System.out.println(array.get(0));//java
//要是下标越界就会异常
//public int size()返回后集合中的元素个数
System.out.println(array.size());//2
}
}
ArrayList对类的操作
public class test {
public static void main(String[] args) {
ArrayList<Student> arrs = new ArrayList<Student>();
arrs.add(new Student("lll", 20));
arrs.add(new Student("rrr", 23));
for(int i=0;i<arrs.size();i++){
System.out.println(arrs.get(i).getName()+" "+arrs.get(i).getAge());
}
}
}
3. Collection(单列集合)
张三 |
---|
李四 |
王五 |
3.1 Collection集合的概述和使用
Collection集合的概述
- 是单列集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
- JDK不提供此类的任何直接实现,它提供更具体的子接口(如Set和List)实现
创建Collection集合的对象
- 多态的方式(父类对象指向子类)
- 具体的实现类ArrayList
import java.util.ArrayList;
import java.util.Collection;
public class test {
public static void main(String[] args) {
//创建Collection集合的对象,用多态的方法
Collection<String> c = new ArrayList<String>();
//添加元素 boolean add (E e)
c.add("hello");
c.add("world");
c.add("java");
//输出集合对象,重写了toString方法
System.out.println(c);//输出:[hello, world, java]
}
}
3.2 Collection集合常用方法
方法名 | 说明 |
---|---|
boolean add (E e) | 添加元素 |
boolean remove (Object o) | 从集合中移除指定的元素 |
void clear( ) | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty( ) | 判断集合是否为空 |
int size( ) | 集合的长度,也就是集合中元素的个数 |
import java.util.ArrayList;
import java.util.Collection;
//Alt + 7可以打开一个窗口,看到类的所有消息
public class test {
public static void main(String[] args) {
//创建Collection集合的对象,用多态的方法
Collection<String> c = new ArrayList<String>();
//添加元素 boolean add (E e)
c.add("hello");
c.add("world");
System.out.println(c.add("world"));//如果添加成功就返回true
//输出:true
//对于Collection的ArrayList来说,调用add永远返回true
//输出集合对象,重写了toString方法
System.out.println(c);//输出:[hello, world, world],可以存储重复元素
//boolean remove (Object o) 从集合中移除指定的元素
System.out.println(c.remove("hello"));//true
System.out.println("java");//false
System.out.println(c);//[world, world]
//int size( ) 集合的长度,也就是集合中元素的个数
System.out.println(c.size());//2
//boolean contains(Object o)判断集合中是否存在指定的元素
System.out.println(c.contains("world"));//true
System.out.println(c.contains("java"));//false
//void clear( ) 清空集合中的元素
c.clear();
System.out.println(c);//输出:[]
//boolean isEmpty( )判断集合是否为空
System.out.println(c.isEmpty());//true清空后,集合为空,返回true
}
}
3.3 Collection集合的遍历
3.3.1 Iterator 迭代器(是一个接口)
Iterator:迭代器,集合的专用遍历方式
- Iterator iterator( ): 返回此集合中元素的迭代器,通过集合的iterator( )方法得到
- 迭代器是通过集合的iterator( )方法得到的,所以迭代器是依赖于集合存在的。
Iterator中的常用方法:
- E next( ):返回迭代中的下一个元素
- boolean hasNext( ):如果迭代具有更多元素,则返回true
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class test {
public static void main(String[] args) {
Collection<String> c =new ArrayList<String>();
c.add("hello");
c.add("java");
c.add("world");
//Iterator<E> iterator( ): 返回此集合中元素的迭代器,通过集合的iterator( )方法得到
Iterator<String> it = c.iterator();
//E next()返回迭代器下一个元素
/*System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());//NoSuchElementException 被请求的元素不存在*/
if(it.hasNext()){
System.out.println(it.next());
}//这样写就不会抛出异常
//用while循环改进
while(it.hasNext()){
// System.out.println(it.next());
String s= it.next();
System.out.println(s);
}
//输出:
// hello
//java
//world
}
}
3.4 集合的使用步骤
- 步骤1:创建集合对象
- 步骤2:添加元素
- 步骤2.1:创建元素
- 步骤2.2:添加元素到集合
- 合并:添加元素到集合
- 步骤3:遍历集合
- 步骤3.1:通过集合对象获取迭代器对象
- 步骤3.2:通过迭代器对象的hasNext( )方法判断是否还有元素
- 步骤3.3:通过迭代器对象的next( )方法获取下一个元素
public class test {
public static void main(String[] args) {
Collection<String> c =new ArrayList<String>();
String s = "lalala";
c.add(s);
Iterator<String> it = c.iterator();
while (it.hasNext()) {
String str = it.next();
System.out.println(str);
}
}
}
4. List
4.1 List 集合概述和特点
在java.util包下
List集合概述:
- 有序集合(也称为序列),用户可以精确控制列表中每一个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同。列表通常允许重复的元素
List集合的特点:
- 有序:存储和取出的元素顺序一致
- 可重复:存储的元素可以重复
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class test {
public static void main(String[] args) {
//创建List集合对象,用多态的方式,父类对象指向子类,因为List没有构造方法,只是一个接口
List<String> l = new ArrayList<>();
//添加
l.add("javase");
l.add("javaee");
l.add("java");
l.add("java");
//迭代器遍历List
Iterator<String> it = l.iterator();
while(it.hasNext()){
String s= it.next();
System.out.println(s);
/*
输出:
javase
javaee
java
java
*/
}
}
}
4.2 List集合特有方法(同ArrayList)
方法名 | 说明 |
---|---|
void add(int index,E element) | 在此集合中指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
mport java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class test {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("java");
list.add("hello");
list.add("javaee");
// void add(int index,E element) 在此集合中指定位置插入指定的元素
list.add(1,"javase");
// list.add(11,"jaaaa");异常报错,IndexOutOfBoundsException
System.out.println(list);
//E remove(int index) 删除指定索引处的元素,返回被删除的元素
list.remove(1);
// list.remove(11);IndexOutOfBoundsException
System.out.println(list);
//E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
list.set(2,"javabest");
// list.set(11,"hello");IndexOutOfBoundsException
System.out.println(list);
//E get(int index) 返回指定索引处的元素
System.out.println(list.get(1));
// System.out.println(list.get(11));IndexOutOfBoundsException
//利用索引遍历list
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
/*
输出:
[java, javase, hello, javaee]
[java, hello, javaee]
[java, hello, javabest]
hello
*/
}
//遍历Student类
//迭代器遍历
while(it.hasNext()){
Student s =it.next();
System.out.println(it.getName()+it.getAge());
}
//for循环遍历
for(int i=0;i<list.size();i++){
Student s =list.get(i);
System.out.println(it.getName()+it.getAge());
}
4.3 并发修改异常
-
ConcurrentModificationException报出异常,当不允许这样修改时,可以检测到对象的并发修改方法来抛出异常
产生原因:
- 迭代器遍历的过程中,通过集合对象修改了集合中元素的长度,造成迭代器获取元素中判断预期修改值和实际修改值不一致,这里详见IT黑马
解决方案
- 用for循环遍历,然后用集合对象做对应的操作即可
/*
需求:
有一个List集合
里面有三个元素"hello","world","java"
遍历集合,每得到一个元素,看看有没有”world“这个单词,如果有,我们就添加”javaee“元素
ConcurrentModificationException报出异常,当不允许这样修改时,可以检测到对象的并发修改方法来抛出异常
*/
public class test {
public static void main(String[] args) {
//有一个List集合
List<String> list = new ArrayList<String>();
//里面有三个元素"hello","world","java"
list.add("hello");
list.add("world");
list.add("java");
//遍历集合,每得到一个元素,看看有没有”world“这个单词,如果有,我们就添加”javaee“元素
Iterator<String> it = list.iterator();
while(it.hasNext()){
String s = it.next();//这里出了异常
if(s.equals("world")){
list.add("javaee");
}
}
//ConcurrentModificationException报出异常,当不允许这样修改时,可以检测到对象的并发修改方法来抛出异常
//可以用for循环代替来解决问题
for(int i=0;i<list.size();i++){
String s= list.get(i);
if(s.equals("world")){
}
}
System.out.println(list);
}
}
4.4 ListIterator
ListIterator:列表迭代器,在java.util
- 通过List集合的listIterator()方法得到,所以说它是List集合特有的迭代器
- 用于允许程序员沿任意方向遍历列表的列表迭代器,在迭代期间修改列表,,并获取列表中迭代器的当前位置
ListIterator中的常用方法
- E next():返回迭代中的下一个元素
- boolean hasNext():如果迭代具有更多元素,则返回true
- E previous():返回列表中的上一个元素
- boolean hasPrevious():如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回true
- void add(E e):将指定元素插入列表
import java.util.*;
public class test {
public static void main(String[] args) {
//有一个List集合
List<String> list = new ArrayList<String>();
//里面有三个元素"hello","world","java"
list.add("hello");
list.add("world");
list.add("java");
/*
//ListIterator迭代器
ListIterator<String> lit = list.listIterator();
// E next():返回迭代中的下一个元素
// boolean hasNext():如果迭代具有更多元素,则返回true
while(lit.hasNext()){
String s = lit.next();
System.out.println(s);
}
System.out.println("-----------------");
// E previous():返回列表中的上一个元素
// boolean hasPrevious():如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回true
while(lit.hasPrevious()){
String s = lit.previous();
System.out.println(s);
}
输出:
hello
world
java
-----------------
java
world
hello
*/
//ListIterator最主要的功能时可以弥补Iterator的并发修改异常
ListIterator<String> lit = list.listIterator();
while(lit.hasNext()){
String s= lit.next();
if(s.equals("world")){
lit.add("javaee");//注意:是在当前这个下标值后面追加信息
}
}
System.out.println(list);//[hello, world, javaee, java]
}
}
4.5 增强for循环
增强for循环:简化数组和Collection集合的遍历
- 实现Iterator接口的类允许其对象成为增强型for语句的目标
- 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
增强for的格式:
- 格式:
for(元素数据类型 变量名 :数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
}
public static void main(String[] args){
//数组
int[] arr = {1,2,3,4,5};
for(int i:arr){
System.out.println(i);
}
//集合
List<String> list = new ArrayList<String>();
list.add("hello");
list.add("java");
for(String s : list){
System.out.println(s);
}
//内部是一个Iterator迭代器
for(String s : list){
if(s.equals("world")){
list.add("java");
}
}//这里抛出并发修改异常
}
4.6 常用数据结构
栈:后进先出,先进后出
队列:先进先出,后进后出
数组:查询快,增删慢
链表:查询慢,增删快
4.7 List集合子类特点
List常用子类:ArrayList,LinkedList
- ArrayList:底层数据结构是数组,查询快,增删慢。
- LinkedList:底层数据结构是链表,查询慢,增删快。
两个子类的用法相同,但是特点不同
- ArrayList
public class test {
public static void main(String[] args) {
//ArrayList
ArrayList<String> arr = new ArrayList<String>();
//add
arr.add("hello");
arr.add("java");
arr.add("world");
//遍历
for (String s : arr) {
System.out.println(s);
}
}
}
- LinkedList
public class test {
public static void main(String[] args) {
//LinkedList
LinkedList<String> linklist = new LinkedList<String>();
//add
linklist.add("hello");
linklist.add("java");
linklist.add("world");
//遍历
for (String s : linklist) {
System.out.println(s);
}
}
}
4.8 LinkedList集合的特有功能
方法名 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定元素 |
public void addLast(E e) | 将指定元素追加到此列表的末尾 |
public E getFirst() | 返回此列表的第一个元素 |
public E getLast() | 返回此列表的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
publci E removeLast() | 从此列表中删除并返回最后一个元素 |
public class test {
public static void main(String[] args) {
//LinkedList
LinkedList<String> linklist = new LinkedList<String>();
//add
linklist.add("hello");
linklist.add("java");
linklist.add("world");
System.out.println(linklist);//[hello, java, world]
//| public void addFirst(E e) | 在该列表开头插入指定元素 |
//| public void addLast(E e) | 将指定元素追加到此列表的末尾 |
linklist.addFirst("JAVAEE");
linklist.addLast("JAVAsE");
System.out.println(linklist);//[JAVAEE, hello, java, world, JAVAsE]
//| public E getFirst() | 返回此列表的第一个元素 |
//| public E getLast() | 返回此列表的最后一个元素 |
System.out.println(linklist.getFirst());//JAVAEE
System.out.println(linklist.getLast());//JAVAsE
//| public E removeFirst() | 从此列表中删除并返回第一个元素 |
//| publci E removeLast() | 从此列表中删除并返回最后一个元素 |
linklist.removeFirst();
linklist.removeLast();
System.out.println(linklist);//[hello, java, world]
}
}
5. Set
5.1 Set集合概述和特点
Set集合特点:
- 不包含重复元素的集合
- 没有带索引方法,所以不能使用for循环遍历
- HashSet对迭代循序不做任何保证
public class test {
public static void main(String[] args) {
//创建Set集合,用多态的方式创建
Set<String> set = new HashSet<String>();
//添加元素,hashset不能保证按顺序输出
set.add("hello");
set.add("world");
set.add("java");
//没有重复元素
set.add("java");
//没有索引,不能用普通for循环遍历,需要用迭代器遍历
for(String s : set){
System.out.println(s);
}
/*
输出:
world
java
hello
*/
}
}
5.2 哈希值
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值
- public int hashCode():返回对象的哈希码值
对象的哈希值特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
public class test {
public static void main(String[] args) {
Student s = new Student("lhp",20);
//同一个对象的hashcode相同
System.out.println(s.hashCode());//460141958
System.out.println(s.hashCode());//460141958
//一般情况下,不同对象的hashcode不同
//但是要是类中重写hashcode方法,就可以实现不同对象同一个hashcode
Student s2= new Student("lrx",24);
System.out.println(s2.hashCode());//1163157884
System.out.println("java".hashCode());//3254818
System.out.println("java".hashCode());//3254818
System.out.println("world".hashCode());//113318802
//这里字符产重写了hashcode
System.out.println("重地".hashCode());//1179395
System.out.println("通话".hashCode());//1179395
}
}
5.3 HashSet集合概述和特点
HashSet集合特点
- 底层数据结构是哈希表
- 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出元素顺序一致
- 没有带索引的方法,所以不能使用普通的for循环遍历
- 由于是Set集合,所以是不包含重复元素的集合
public class test {
public static void main(String[] args) {
HashSet<String> hs = new HashSet<String>();
//添加
hs.add("java");
hs.add("java");
hs.add("javaee");
hs.add("javase");
//遍历
for (String h : hs) {
System.out.println(h);
}
/*
输出:
java
javase
javaee
*/
}
}
HashCode存储元素:
- 要保证元素的唯一性,需要重写hashCode()和equals()
5.4 数据结构之哈希表
哈希表
- JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
- JDK8以后,在长度较长的时候,底层实现了优化
详见it黑马
5.5 HashSet集合存储学生对象(如果要保证唯一性,需要重写equals和hashcode方法)
public class Student {
private String name;
private int age;
public Student(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;
}
//重写equals方法,可以让HashSet添加对象时表现出唯一性,不重复性
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
//重写hashCode方法
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
}
public class test {
public static void main(String[] args) {
//创建关于学生的哈希表
HashSet<Student> hs = new HashSet<Student>();
//创建学生对象
Student s1= new Student("lhp",20);
Student s2 = new Student("zkt",21);
Student s3= new Student("lhp",20);
//hashset添加对象
hs.add(s1);
hs.add(s2);
hs.add(s3);
//遍历
for (Student h : hs) {
System.out.println("name:"+ h.getName()+" age:"+ h.getAge());
}
/*
没有重写equals方法前:
name:lhp age:20
name:zkt age:21
name:lhp age:20
*/
/*
重写equals方法后:
name:lhp age:20
name:zkt age:21
*/
}
}
5.6 LinkedHashSet集合概述和特点
LinkedHashSet集合特点:
- 哈希表和链表实现的Set接口,具体有可预测的迭代次序
- 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
- 由哈希表保证元素唯一,也就是说没有重复的元素
public class test {
public static void main(String[] args) {
//创建集合
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
//添加元素
linkedHashSet.add("java");
linkedHashSet.add("java");
linkedHashSet.add("javaEE");
linkedHashSet.add("javaSE");
//遍历
for (String s : linkedHashSet) {
System.out.println(s);
}
/*
输出:
java
javaEE
javaSE
*/
}
}
5.7 TreeSet集合概述和特点
TreeSet集合特点:
- 元素有序,这里的顺序不是指存储和取出顺序,而是按照一定规律进行排序,具体排序方式取决于构造方法
- TreeSet():根据其元素的自然排序进行排序
- TreeSet(Comparator comparator):根据指定的比较器进行排序
- 没有索引的方法,所以不能使用普通for循环遍历
- 由于Set集合,所以不包含重复元素的集合
public class test {
public static void main(String[] args) {
//创建对象
TreeSet<Integer> tr = new TreeSet<Integer>();
//添加成员
tr.add(20);
tr.add(10);
tr.add(50);
tr.add(30);
tr.add(70);
tr.add(20);
//遍历
for (Integer integer : tr) {
System.out.println(integer);
}
/*
输出:
10
20
30
50
70
*/
}
}
5.8 自然排序Comparable的使用
- 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student(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;
}
//重写equals方法,可以让HashSet添加对象时表现出唯一性,不重复性
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
//!!!重写Coparable接口里面的方法
@Override
public int compareTo(Student o) {
// return 0;//表示只添加一个元素
// return 1;表示按正序添加和输出元素(升序)
// return -1;//表示按逆序添加和输出元素(降序)
int num1=this.age-o.age;
int num2= num1==0?this.name.compareTo((o.name)):num1;//如果年龄相同就比姓名首字母
return num2;
}
}
public class test {
public static void main(String[] args) {
TreeSet<Student> tr = new TreeSet<Student>();
Student s1 = new Student("lhp",20);
Student s2 = new Student("arx",24);
Student s3= new Student("zkt",19);
Student s4= new Student("akt",19);
tr.add(s1);
tr.add(s2);
tr.add(s3);
tr.add(s4);
//这里要实现接口Comparable,并且需要根据要求重写compareto
for (Student student : tr) {
System.out.println(student.getName()+" "+student.getAge());
}
/*
输出:
akt 19
zkt 19
lhp 20
arx 24
*/
}
}
结论:
- 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
- 自然排序,就是让元素所属的类实现Comparable接口,重写cmopareTo(T o)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
5.9 比较器排序Comparable的使用
- 存储学生对象并遍历,创建TreeSet集合使用有参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
import java.util.*;
/*
- 存储学生对象并遍历,创建TreeSet集合使用**有参构造方法**
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
*/
public class test {
public static void main(String[] args) {
//创建集合的时候,使用有参构造
TreeSet<Student> tr = new TreeSet<Student>(new Comparator<Student>() {
//方法重写
@Override
public int compare(Student o1, Student o2) {
//主要条件
int num1=o1.getAge()-o2.getAge();
//次要条件s
int num2 = num1==0?o1.getName().compareTo(o2.getName()):num1;
return num2;
}
});
Student s1 = new Student("lhp",20);
Student s2 = new Student("arx",24);
Student s3= new Student("zkt",19);
Student s4= new Student("akt",19);
tr.add(s1);
tr.add(s2);
tr.add(s3);
tr.add(s4);
//这里要实现接口Comparable,并且需要根据要求重写compareto
for (Student student : tr) {
System.out.println(student.getName()+" "+student.getAge());
}
/*
输出:
akt 19
zkt 19
lhp 20
arx 24
*/
}
}
结论:
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序
- 比较器排序,就是让集合构造方法接收Comparator的实现对象,重写compare(T o1,T o2)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
注意:
Set set = new HashSet();//不会排序
SetSet=new TreeSet();//会排序
6. 泛型
6.1 泛型的概述
泛型:是JDK5中引入的特性,它提供了编译时类型安全检查机制,该机制允许在编译时检测到非法的类型,它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参,那么参数化类型怎么理解?
顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型
这种参数类型可以用在类、方法和接口中,分别被称为泛型类,泛型方法、泛型接口**fanx
泛型定义格式:
- <类型>:指定一种类型的格式。这里的类型可以看成是形参
- <类型1,类型2…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
- 将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
泛型的好处:
- 把运行时期的问题提前到了编译期间
- 避免了强制类型转换
public static void main(String[] args){
Collection c = new ArrayList();//这里是object
Collection<String> c = new ArrayList<String>();//这里是String泛型
....
}
6.2 泛型类
泛型类的定义格式:
- 格式:修饰符 class 类名<类型>{ }
- 范例:public class Generic{}
- 此处T可以随便写为任意标识,常见的如T,E,K,V等形式的参数常用于表示泛型
public class Generic<T> {
private T t;
public Generic() {
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
public class test {
public static void main(String[] args) {
Generic<String> g = new Generic<String>();
g.setT("lalala");
System.out.println(g.getT());//输出lalala
Generic<Integer> gg = new Generic<Integer>();
gg.setT(100);
System.out.println(gg.getT());//输出100
}
}
6.3 泛型方法
泛型方法的定义格式:
- 格式: 修饰符<类型> 返回值类型 方法名 (类型 变量名){ }
- 范例: public < T> void show(T t){ }
public class Generic{
//泛型方法
public<T> void show(T t){
System.out.println(t);
}
}
public class test {
public static void main(String[] args) {
Generic g= new Generic();
g.show(120);
g.show("lhlp");
g.show(true);
}
/*
输出:
120
lhlp
true
*/
}
6.4 泛型接口
泛型接口的定义格式:
- 格式:修饰符 interface 接口名 <类型>{ }
- 范例: public interface Generic { }
public interface Generic<T> {
void show(T t);
}
public class Genericimpl<T> implements Generic<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
public class test {
public static void main(String[] args) {
//通过类来实现和创建接口对象
Generic<String> g = new Genericimpl<String>();
g.show("llllpppp");//输出:llllpppp
}
}
6.5 类型通配符
为了表示各种泛型List的父类,可以使用类型通配符
- 类型通配符:<?>
- List<?>:表示元素类型未知的List,它的元素可以匹配任何类型
- 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
如果说我们不希望List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父类,可以使用类型通配符的上限
- 类型通配符上线:<? extends 类型>
- List<? extends Number>:他表示的类型是Number或者其子类型
除了可以指定类型通配符的上限,我们也可以指定类型通配符的下限
- 类型通配符下限:<? super 类型>
- List<?super Number>:他表示的类型是Number或者其父类型
public class test {
public static void main(String[] args) {
//类型通配符:**<?>**
List<?> list1 = new ArrayList<Object>();
List<?> list2 = new ArrayList<Number>();
List<?> list3 = new ArrayList<Integer>();
//类型通配符上线:**<? extends 类型>**
// List<? extends Number> list4 =new ArrayList<Object>();报错
List<? extends Number> list5=new ArrayList<Number>();
List<? extends Number> list6=new ArrayList<Integer>();
//类型通配符下限:**<? super 类型>**
List<? super Number> list7 = new ArrayList<Number>();
// List<? super Number> list8 = new ArrayList<Integer>();报错
List<? super Number> list9 = new ArrayList<Object>();
}
}
6.6 可变参数
可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
- 格式:修饰符 返回值类型 方法名(数据类型… 变量名)
- 范例: public static int sum(int… a)
可变参数注意事项:
- 这里的变量其实是一个数组
- 如果一个方法有多个参数,包含可变参数,可变参数一定要放在最后
public class test {
public static void main(String[] args) {
System.out.println(sum(10,20,30));//60
System.out.println(sum(10,20,30,40,50));//150;
System.out.println(sum(10,20,30,40,50,60,70));//280
System.out.println(sum(10,20,30,40,50,60,70,80,90));//450
}
public static int sum(int... a){
int sum=0;
for (int i : a) {
sum+=i;
}
return sum;
}
}
6.7 可变参数的使用
Arrays工具中有一个静态方法:
- public static List asList(T… a):返回由指定数组支持的固定大小的列表
- 返回的集合不能做增删操作,可以做修改操作
public class test {
public static void main(String[] args) {
List<String> list = Arrays.asList("hello","java","javaee");
// list.add("javaee");//UnsupportedOperationException
// list.remove("java");//UnsupportedOperationException
list.set(0,"javaee");
System.out.println(list);
//无法增删,但是可以修改
/*
输出:[javaee, java, javaee]
*/
}
}
List接口中有一个静态方法:
- public static List of(E… elements):返回包含任意数量元素的不可变列表
- 返回的集合不能做增删改操作
public static void main(String[] args){
List<String> list= list.of("hello","java","javaee");
//list.add("javase");//UnsupportedOperationException
//list.remove("javaee");//UnsupportedOperationException
//list.set(0,"ok");//UnsupportedOperationException
//无法增删改
System.out.println(list);
}
Set接口中有一个静态方法:
- public static Set of(E… elements):返回一个包含任意数量元素的不可变列表
- 在给元素的时候,不能给重复元素
- 返回的集合不能做增删操作,没有修改操作
public static void main(String[] args){
// Set<String> set = Set.of("world","world","java");//IllegalArgumentException
Set<String> set = Set("world","java","hello");
//set.add("javaee");
//set.remove("world");
System.out.println(set);
}
7. Map(双列集合)
001 | 张三 |
---|---|
002 | 李四 |
003 | 王五 |
7.1 Map集合概述和使用
Map集合概述
- Interface Map<K,V> K:键值的类型; V:值的类型;
- 将键映射到值的对象,不能包含重复的键,每个键可以映射到最多一个值
- 举例:学生的学号和姓名
001 mark
002 jake
003 leo
创建Map的对象
- 多态的方式
- 具体的实现类HashMap
public class test {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
map.put("001","leo");
map.put("002","nardor");
map.put("003","lili");
map.put("003","mike");//同一个键值出现两次,第二次会把第一次覆盖掉
System.out.println(map);//{001=leo, 002=nardor, 003=mike}
}
}
7.2 Map集合的基本功能
方法名 | 说明 |
---|---|
V put(K key,V value) | 添加元素 |
V remove(Object Key) | 根据键删除键值元素 |
void clear( ) | 移除所有的键值元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object key) | 判断集合是否包含指定的值 |
boolean isEmpty( ) | 判断集合是否为空 |
int size( ) | 集合的长度,也就是集合中键值对的个数 |
public class test {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
//V put(K key,V value) | 添加元素
map.put("001","llll");
map.put("002","lele");
map.put("003","lplp");
//V remove(Object Key) | 根据键删除键值元素
System.out.println(map.remove("001"));//输出:llll
System.out.println(map.remove("004"));//输出:null
//boolean containsKey(Object key) | 判断集合是否包含指定的键
System.out.println(map.containsKey("002"));//true
System.out.println(map.containsKey("005"));//false
//boolean containsValue(Object key) | 判断集合是否包含指定的值
System.out.println(map.containsValue("llll"));//false
System.out.println(map.containsValue("lele"));//true
//boolean isEmpty( ) | 判断集合是否为空
System.out.println(map.isEmpty());//false
//int size( ) | 集合的长度,也就是集合中键值对的个数
System.out.println(map.size());//2
//void clear( ) | 移除所有的键值元素
System.out.println(map);//输出:{002=lele, 003=lplp}
map.clear();
System.out.println(map);//输出:{}
}
}
7.3 Map集合的获取功能
方法名 | 说明 |
---|---|
V get(Object key) | 根据键获取值 |
Set keySet( ) | 获取所有键的集合 |
Collection values( ) | 获取所有值的集合 |
Set<Map.Entry<K,V> > entrySet( ) | 获取所有键值对对象的集合 |
public class test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("001", "llll");
map.put("002", "lele");
map.put("003", "lplp");
//V get(Object key) | 根据键获取值
System.out.println(map.get("001"));//llll
System.out.println(map.get("004"));//null
//Set<K> keySet( ) | 获取所有键的集合
Set<String> keyset = map.keySet();
for (String s : keyset) {
System.out.println(s);
/*
输出:
001
002
003
*/
}
//Collection<V> values( ) | 获取所有值的集合
Collection<String> values = map.values();
for (String value : values) {
System.out.println(value);
/*
llll
lele
lplp
*/
}
//Set<Map.Entry<K,V> > entrySet( )获取所有键值对对象的集合
Set<Map.Entry<String,String>> mapentry = map.entrySet();
// 2. 遍历键值对对象的集合,得到每一个键值对对象**用增强for实现,得到每一个Map.Entry**
for (Map.Entry<String, String> entry : mapentry) {
// 3. 根据键值对对象获取键和值:用**getKKey( )**得到键,用**getValue( )**得到值
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+" "+value);
}
}
}
7.4 Map集合的遍历
7.4.1遍历方法1:
把Map看成事夫妻对的集合
遍历思路:
-
把所有丈夫给集中起来
-
遍历丈夫的集合,获取到每一个丈夫
-
根据丈夫去找对应的妻子
转换为Map集合中的操作:
-
获取所有键的集合,用keySet() 方法实现
-
遍历键的集合,获取到每一个键。增强for实现
-
根据键去找值,用get(Object key)方法实现
public class test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("001", "llll");
map.put("002", "lele");
map.put("003", "lplp");
//1. 获取所有键的集合,用keySet() 方法实现
Set<String> keyset=map.keySet();
//2. 遍历键的集合,获取到每一个键。增强for实现
for (String s : keyset) {
//3. 根据键去找值,用get(Object key)方法实现
String values=map.get(s);
System.out.println(s+" "+values);
}
/*
001 llll
002 lele
003 lplp
*/
}
}
7.4.2 遍历方法2:
把Map看成是一个夫妻对
遍历思路:
- 获取所有结婚证的集合
- 遍历结婚证的集合,得到每一个结婚证
- 根据结婚证获取丈夫和妻子
转换为Map集合中的操作:
- 获取所有键值对对象的集合Set<Map.Entry<K,V> > entrySet( ):获取所有键值对对象的集合
- 遍历键值对对象的集合,得到每一个键值对对象用增强for实现,得到每一个Map.Entry
- 根据键值对对象获取键和值:
- 用**getKKey( )**得到键
- 用**getValue( )**得到值
public class test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("001", "llll");
map.put("002", "lele");
map.put("003", "lplp");
// 1. 获取所有键值对对象的集合**Set<Map.Entry<K,V> > entrySet( ):获取所有键值对对象的集合**
Set<Map.Entry<String,String>> mapentry = map.entrySet();
// 2. 遍历键值对对象的集合,得到每一个键值对对象**用增强for实现,得到每一个Map.Entry**
for (Map.Entry<String, String> entry : mapentry) {
// 3. 根据键值对对象获取键和值:用**getKKey( )**得到键,用**getValue( )**得到值
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+" "+value);
}
}
/*
001 llll
002 lele
003 lplp
*/
}
//Map.Entry看作是一个Map对象
7.5 典型案例
需求:创建一个HashMap集合,键是学生对象(student),值是居住地(String)。存储多个键值对元素,并且遍历,要求保证键的唯一性:如果学生对象的成员变量值相同,我们就认为是同一个对象
思路:
- 定义学生类
- 创建HashMap集合对象
- 创建学生对象
- 把学生添加到集合
- 遍历集合
- 在学生类中重写两个方法hashCode()和equals()
public class Student {
private String Name;
private int Age;
public Student(String name, int age) {
Name = name;
Age = age;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public int getAge() {
return Age;
}
public void setAge(int age) {
Age = age;
}
public Student() {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (Age != student.Age) return false;
return Name != null ? Name.equals(student.Name) : student.Name == null;
}
@Override
public int hashCode() {
int result = Name != null ? Name.hashCode() : 0;
result = 31 * result + Age;
return result;
}
}
public class test {
public static void main(String[] args) {
Map<Student, String> map = new HashMap<Student, String>();
Student s1 = new Student("www", 30);
Student s2 = new Student("ttt", 33);
Student s3 = new Student("jjj", 35);
Student s4 = new Student("jjj", 35);
//如果不重写学生类的两个方法,Set集合中相同的内容将无法覆盖
/*不重写方法输出:
jjj,35,河南
ttt,33,浙江
www,30,北京
jjj,35,江苏
*/
map.put(s1, "北京");
map.put(s2, "浙江");
map.put(s3, "河南");
map.put(s4, "江苏");
Set<Student> keyset = map.keySet();
for (Student student : keyset) {
String value = map.get(student);
System.out.println(student.getName() + "," + student.getAge() + "," + value);
}
/*重写方法后的输出:
www,30,北京
jjj,35,江苏
ttt,33,浙江
*/
}
}
统计字符串中每个字符出现的次数
public class test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
//HashMap<Character,Integer> hm = new HashMap<Character, Integer>();无序版本
TreeMap<Character,Integer> hm = new TreeMap<Character, Integer>();//有序版本
for(int i=0;i<s.length();i++){
Character key = s.charAt(i);
Integer value = hm.get(key);
if(value==null){
hm.put(key,1);
}else{
value++;
hm.put(key,value);
}
}
//拼接
StringBuilder sb = new StringBuilder();
Set<Character> keyset = hm.keySet();
for (Character character : keyset) {
Integer value = hm.get(character);
sb.append(character).append("(").append(value).append(")");
}
String res = sb.toString();
System.out.println(res);
}
}
7.6 集合嵌套
7.6.1 集合嵌套之ArrayList嵌套HashMap
- ArrayLlist集合存储HahsMap元素并遍历
需求:创建一个ArrayList集合,存储三个元素,每一个元素都是HashMap,每一个HashMap的键和值都是String,并且遍历
思路:
- 创建ArrayList集合
- 创建HashMap集合,并添加键值对元素
- 把HashMap作为元素添加到ArrayList集合
- 遍历ArrayList集合
public class test {
public static void main(String[] args) {
// 1. 创建ArrayList集合
ArrayList<HashMap<String,String>> arrayList = new ArrayList<HashMap<String, String>>();
// 2. 创建HashMap集合,并添加键值对元素
HashMap<String,String> hm1= new HashMap<String, String>();
hm1.put("001","lll");
hm1.put("002","ooo");
HashMap<String,String> hm2= new HashMap<String, String>();
hm2.put("001","rrr");
hm2.put("002","ttt");
HashMap<String,String> hm3= new HashMap<String, String>();
hm3.put("001","vvv");
hm3.put("002","qqq");
// 3. 把HashMap作为元素添加到ArrayList集合
arrayList.add(hm1);
arrayList.add(hm2);
arrayList.add(hm3);
// 4. 遍历ArrayList集合
for (HashMap<String, String> hashMap : arrayList) {//每一次都是一个hashmap
//按照hashmap集合遍历的方式遍历
Set<String> keyset = hashMap.keySet();
for (String key : keyset) {
String value = hashMap.get(key);
System.out.println(key+","+value);
}
}
}
}
7.6.2 集合嵌套之HashMap嵌套ArrayList
- HashMap集合存储ArrayList元素并遍历
需求:创建一个HashMap集合,存储三个键值对元素,每一个键值对元素的键是String,值是ArrayList,每一个ArrayList元素是String,并且遍历
思路:
- 创建HashMap集合
- 创建ArrayList集合,并添加元素
- 把ArrayList作为元素添加到HashMap集合
- 遍历HashMap集合
public class test {
public static void main(String[] args) {
// 1. 创建HashMap集合
HashMap<String,ArrayList<String>> hashMap = new HashMap<String, ArrayList<String>>();
// 2. 创建ArrayList集合,并添加元素
ArrayList<String> arr1 = new ArrayList<String>();
arr1.add("javaee");
arr1.add("javase");
ArrayList<String> arr2 = new ArrayList<String>();
arr2.add("C");
arr2.add("C++");
ArrayList<String> arr3 = new ArrayList<String>();
arr3.add("HTML");
arr3.add("CSS");
// 3. 把ArrayList作为元素添加到HashMap集合
hashMap.put("JAVA",arr1);
hashMap.put("CPP",arr2);
hashMap.put("WEP",arr3);
// 4. 遍历HashMap集合
Set<String> keyset= hashMap.keySet();
for (String s : keyset) {
ArrayList<String> value = hashMap.get(s);
System.out.print(s+" ");
for (String s1 : value) {
System.out.print(s1+" ");
}
System.out.println();
}
}
/*
输出:
JAVA javaee javase
CPP C C++
WEP HTML CSS
*/
}
8. Collections
8.1 Collections概述和使用
Collections类的概述
- 是针对集合操作的工具类,类似于Arrays的用法,之间用类名调用方法
Collections类常用的方法
- public static <T entends Comparable<? super T> > void sort(List list):将指定的列表按照升序排序
- public static void reverse(List<?> list):反转指定列表中元素的顺序
- public static void shuffle<List<?> list):使用默认的随机源随机排列指定的列表
public class test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(10);
list.add(4);
list.add(30);
list.add(25);
list.add(100);
System.out.println(list);//[10, 4, 30, 25, 100]
//reverse反转
Collections.reverse(list);
System.out.println(list);//[100, 25, 30, 4, 10]
//sort排序
Collections.sort(list);
System.out.println(list);//[4, 10, 25, 30, 100]
//shuffl随机排序
Collections.shuffle(list);
System.out.println(list);//[30, 10, 25, 100, 4]
}
}
案例:ArrayList存储学生对象并排序
需求:ArrayList存储学生对象,使用Collections对ArrayList进行排序
要求:按照年龄从小到大排序,年龄相同,按照姓名的字母顺序排序
(之前使用TreeSet实现的,现在用Collections实现)
思路:
- 定义学生类
- 创建ArrayList集合对象
- 创建学生对象
- 把学生添加到集合
- 使用Collections对ArrayList集合排序
- 遍历集合
public class test {
public static void main(String[] args) {
// 1. 定义学生类
// 3. 创建学生对象
Student s1 = new Student("lll",20);
Student s2 = new Student("ooo",30);
Student s3 = new Student("sss",19);
Student s4 = new Student("kkk",19);
// 2. 创建ArrayList集合对象
// 4. 把学生添加到集合
ArrayList<Student> stu = new ArrayList<Student>();
stu.add(s1);
stu.add(s2);
stu.add(s3);
stu.add(s4);
// 5. 使用Collections对ArrayList集合排序
Collections.sort(stu,new Comparator<Student>(){
@Override
public int compare(Student o1, Student o2) {
int num1=o1.getAge()-o2.getAge();
int num2 = num1==0?o1.getName().compareTo(o2.getName()):num1;
return num2;
}
});
// 6. 遍历集合
for (Student student : stu) {
System.out.println(student.getAge()+","+student.getName());
}
/*
输出:
19,kkk
19,sss
20,lll
30,ooo
*/
}
}