正因为上面的缺点,引入了一个新的存储数据的结构:集合
通过实现类创建对象:Collection collection = new ArrayList();//不能用子类特有的方法
Collection对象的方法(子类通用) | 说明 | 返回值 |
---|---|---|
add(E e) | 向集合添加元素e,若指定集合元素改变了则返回true | boolean |
addAll(Collection<? extends E> c) | 把集合C的元素全添加到集合中,若指定集合元素改变返回true | boolean |
clear() | 清空所有集合元素 | void |
contains(Object o) | 判断指定集合是否包含对象o | boolean |
containsAll(Collection<?> c) | 判断指定集合是否包含集合c的所有元素 | boolean |
isEmpty() | 判断指定集合的元素大小是否为0 | boolean |
remove(Object o) | 删除集合中的元素对象o,若有多个o元素,则只删除第一个元素 | boolean |
removeAll(Collection<?> c) | 删除指定集合包含集合c的元素,多个相同元素全删除 | boolean |
retainAll(Collection<?> c) | 从指定集合中保留包含集合c的元素,其他元素则删除 | boolean |
size() | 集合的元素个数 | int |
toArray(T[] a) | 将集合转换为T类型的数组 | T[] |
equals(Collection<?> c) | 比较两个集合中内容是否一样 | boolean |
iterator() | 迭代器,用于循环遍历(每个集合都有对应的迭代器对象iterator) | Iterator |
看下面代码,要求会用
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
public class Demo1 {
public static void main(String[] args) {
//实例化
Collection collection = new ArrayList();//不能用子类特有的方法
ArrayList arrayList = new ArrayList();
//添加元素add()、addAll(),集合可以存储不同类型的元素(很少这么做)
collection.add("1233");
collection.add(123);//自动装箱成Integer
collection.add(123.2);//自动装箱成Double
collection.add("1233");
System.out.println(collection.size());//获得集合中真实的元素个数
System.out.println(collection);//默认调用toString方法
arrayList.addAll(collection);//把集合中的元素全部加到集合里
//迭代器,循环遍历用的(每个集合类型都有对应的迭代器对象iterator,比for效率高)
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()){//判断光标的下一位有没有元素
Object next = iterator.next();
System.out.println(next);
}
System.out.println(Arrays.toString(arrayList.toArray()));//转数组
System.out.println("判断是否有该元素:"+arrayList.contains("123"));//类型也要一样
//删除元素clear()、remove(值)
arrayList.remove("1233");//移出先找到的一个值(只删一个)
System.out.println(arrayList);
System.out.println("移出前的arrayList"+arrayList+"\t移出前的collection"+collection);
collection.removeAll(arrayList);//把该集合所有包含的值都删掉(包括相同的)
System.out.println("移出后的arrayList"+arrayList+"\t移出后的collection"+collection);
arrayList.clear();
System.out.println(arrayList);
System.out.println(arrayList.isEmpty());//判断集合是否为空[],不是null
System.out.println(arrayList.equals(collection));//比较是否相同
}
/*
【集合】(容器)
两个图:
1.(一个一个存,有迭代器)Collection(下标无序,数据可重复)->List(下标有序,数据可重复)、Set(下标无序,数据不可重复)
List->ArrayList(线性表)(Vector是安全的,效率低)、LinkedList(链表)
Set->TreeSet(元素有序)、HashSet、LinkedHashSet元素有序
2.(一对一对存,没有迭代器)Map(key是无序的,数据不可重复,value是无序的,数据可重复)->TreeMap、HashMap
【六个接口】Collection
添加:add(值)、addAll(集合)
删除:clear()、remove(值)、removeAll()
查找:size()、cotains(值)
判断:isEmpty()、equals()
遍历:iterator()
转数组:toArray()
*/
}
迭代器iterator(重点,必须会用)
实例化:Iterator<类型> iterator = 集合对象.iterator();//直接通过集合对象.iterator().var快捷创建即可
iterator.方法() | 说明 | 返回值 |
---|---|---|
hasNext() | 判断光标的下一位有没有元素 | boolean |
next() | 返回下一个元素,并且前进光标位置**还可通过IDEA的.var快捷生成对象 ** | Object |
remove() | 将迭代器返回的元素删除 | void |
迭代和添加操作都是靠ListIterator来完成的:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class Test2 {
//这是main方法,程序的入口
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
list.add("ee");
//在"cc"之后添加一个字符串"kk"
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
if("cc".equals(it.next())){
it.add("kk");
}
}
System.out.println(it.hasNext());
System.out.println(it.hasPrevious());
//逆向遍历:
while(it.hasPrevious()){
System.out.println(it.previous());
}
System.out.println(it.hasNext());
System.out.println(it.hasPrevious());
System.out.println(list);
}
}
ListIterator迭代器方法 | 说明 |
---|---|
next () | 返回列表中的下一个元素,并且前进光标位置 |
previous() | 返回列表中的下一个元素,并且后移动光标位置 |
hasNext() | 判断光标处是否还有下一个元素,有则返回true(正向遍历) |
hasPrevious () | 判断光标处是否还有上一个元素,有则返回true(逆向遍历) |
remove() | 删除一个元素,使用remove时,迭代器的指针不能为初始位置,否则会报错 |
set(E e) | 用指定的元素替换由 next()或 previous()返回的最后一个元素 |
previous () | 返回列表中的上一个元素,并向后 (上)移动光标位置 |
add(E e) | 将指定的元素插入列表的末尾 |
(下标有序,数据可重复(不去重))
通过实现类创建对象:List list = new LinkedList();//不能用子类特有的方法
List接口的特有方法 父类共有方法在上面 | 简介 | 说明(下标注意不能越界) | 返回值 |
---|---|---|---|
add(int index, E ele)` | 增 | 在index位置插入ele元素,下标注意不能越界 | void |
remove(int index) remove(Object o)` | 删 | 删除指定index位置的元素,下标注意不能越界 由于父类有该方法,默认为删除下标的元素 | Object |
set(int index, E ele)` | 改 | 设置指定index位置的元素为ele,下标注意不能越界 | Object |
get(int index)` | 查 | 获取指定index位置的元素 | Object |
addAll(int index, Collection eles) | 插 | 从index位置开始将eles中 的所有元素添加进来 | boolean |
[last]indexOf(Object obj) | 返回obj在集合中首[末]次出现的下标,没有返回-1 | int | |
subList(int fromIndex, int toIndex) | 返回从fromIndex到toIndex 位置的子集合 | List |
ArrayList[ArrayList遍历方式]和LinkedList[LinkedList遍历方式]遍历方式一样,下面不重复写了
List接口的遍历方式 | 使用说明: |
---|---|
Iterator迭代器方式 | Iterator iterator = list对象.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } |
省内存的迭代器方式 | for(Iterator<类型> it = list对象.iterator();it.hasNext(); ){ System.out.println(it.next()); } |
增强for循环,底层也是迭代器实现的 | for(类型 变量名 : list对象){ System.out.println(变量名); } |
普通for循环,少用 | for(int i = 0;i < list对象.size();i++){ System.out.println(list对象.get(i)); } |
循环内获取集合中的单个对象都可通过IDEA的.var快捷生成对象
//遍历方法
ArrayList arrayList1 = new ArrayList(100);//声明了一个容量为100的空数组
arrayList.add("111");
arrayList.add("123");
Iterator iterator = arrayList.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
for(Iterator<String> it = arrayList.iterator();it.hasNext();){
System.out.println(it.next());
}
for(String i : arrayList){
System.out.println(i);
}
for(int i = 0;i < arrayList.size();i++){
System.out.println(arrayList.get(i));
}
数组转集合的方法 | 说明 | 返回值 |
---|---|---|
Arrays.asList(数组) | 将数组转换成集合 | List |
看下面代码,要求会用
public static void main(String[] args) {
List list = new LinkedList();//声明接口new实现类,不能用子类特有的方法
list.add(0,"123");//刚传入的下标位置只能按顺序,否则会下标越界
list.add(123);
list.add(0,"aaa");//添加到第一个位置,其他后移
System.out.println(list);
list.set(0,"bbb");
System.out.println(list.get(0));;//根据下标获取元素
list.remove(2);//根据下标删除,不能下标越界,父类也有该方法,默认安装下标删除
System.out.println("元素ccc下标"+list.indexOf("ccc"));//查找元素,存在返回下标,不存在返回-1
for(Iterator it = list.iterator(); it.hasNext();){
System.out.println("内容:"+it.next());
}
}
/*
List,下面方法是父类没有,自己特有的
添加:add(下标,值)
删除:remove(下标)
查找:get(下标)、indexOf(值)
修改:set(下标,值)
*/
创建对象:ArrayList<引用数据类型> arrayList = new ArrayList<>();
ArrayList遍历方式和LinkedList遍历方式一样,看上面List遍历
ArrayList方法(和List一致) | 说明 | 返回值 |
---|---|---|
add(E e) | 向集合添加元素e,若指定集合元素改变了则返回true | boolean |
add(int index, E ele) | 在index位置插入ele元素,下标注意不能越界 | void |
addAll(Collection<? extends E> c) | 把集合C的元素全添加到集合中,若指定集合元素改变返回true | boolean |
addAll(int index, Collection eles) | 从index位置开始将eles中 的所有元素添加进来 | int |
clear() | 清空所有集合元素 | void |
remove(int index) | 删除指定index位置的元素,下标注意不能越界(默认) | Object |
remove(Object o) | 删除集合中的元素对象o,若有多个o元素,则只删除第一个元素 | boolean |
removeAll(Collection<?> c) | 删除指定集合包含集合c的元素,多个相同元素全删除 | boolean |
retainAll(Collection<?> c) | 从指定集合中保留包含集合c的元素,其他元素则删除 | boolean |
set(int index, E ele) | 设置指定index位置的元素为ele,下标注意不能越界 | Object |
get(int index) | 获取指定index位置的元素 | Object |
[last]indexOf(Object obj) | 返回obj在集合中首[末]次出现的下标,没有返回-1 | boolean |
size() | 集合的元素个数 | int |
contains(Object o) | 判断指定集合是否包含对象o | boolean |
containsAll(Collection<?> c) | 判断指定集合是否包含集合c的所有元素 | boolean |
isEmpty() | 判断指定集合的元素大小是否为0 | boolean |
equals(Collection<?> c) | 比较两个集合中内容是否一样 | boolean |
toArray(T[] a) | 将集合转换为T类型的数组 | T[] |
subList(int fromIndex, int toIndex) | 返回从fromIndex到toIndex 位置的子集合 | List |
iterator() | 迭代器,用于循环遍历(每个集合都有对应的迭代器对象iterator) | Iterator |
和List一样,会用即可
import java.util.ArrayList;
import java.util.Vector;
public class Demo3 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();//底层是声明了一个容量为10的空数组,当空间不够扩容50%,有size变量专门记录数据个数的,数组没有
// Vector arrayList = new Vector();//基本和arrayList一样,扩容1倍
ArrayList arrayList1 = new ArrayList(100);//声明了一个容量为100的空数组
arrayList.add("111");
arrayList.add("123");
System.out.println(arrayList);
}
/*
和List方法一样
1.ArrayList底层是Object[]
2.Object[]初始空间长度是10个,满额后扩容50%
3.ArrayList底层size属性,记录Object[]真实的元素个数
*/
}
和ArrayList用法一致。
线程安全,空间满额后按1倍扩容,早期集合容器(和ArrayList一样,但很少用了)
实现原理相同,功能相同,都是长度可变的
数组结构,很多情况下可以互用
两者的主要区别如下
? Object做参数的话,参数都要向下类型转换,有可能类型不一致导致出错。且不能用子类的特有方法。
? 泛型<>做参数的话,不需要类型转换,类型统一,操作简单(只出现在编译器,运行期消失)(后期反射是运行期,可硬元素)
泛型的类型:都是引用数据类型,不能是基本数据类型。
取出元素时,由于是统一类型,遍历等方面操作简单。
看下面简单的泛型案例
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();//jdk1.7后面可以写成<>或不写了
list.add(999);
list.add(333);
list.add(666);
list.add(1);
for (Integer i:list) {
System.out.print(i+",");
}
}
LinkedList底层是链表
创建对象:LinkeList<引用数据类型> arrayList = new LinkeList<>();
LinkedList遍历方式和ArrayedList遍历方式一样,看上面List遍历
LinkedList常用方法(数据可重复) | 说明红色表示的是特有方法 | 返回值 |
---|---|---|
add(E e)、addLast(E e) | 在链表后添加一个元素e | void |
add(int index, E e) | 在指定下标index插入一个元素e | void |
addFirst(E e) | 在链表头部插入一个元素e | void |
offerFirst(E e) | 在链表头部插入一个元素e | boolean |
offer(E e)、offerLast(E e) | 在链表尾部添加一个元素e | boolean |
poll()、pollFirst()、remove()、removeFirst() | 删除第一个元素并返回(poll为空返回null,remove为空抛异常) | E |
pollLast()、removeLast() | 删除最后一个元素并返回(poll为空返回null,remove为空抛异常) | E |
clear() | 清空链表 | void |
element()、getFirst() | 获取第一个元素(为空抛出异常) | E |
getLast() | 获取最后一个元素 | E |
.get(i) | 获取下标i的对应元素 | E |
[last]indexOf(Object o) | 根据元素查询第一次出现的下标位置 | int |
peek()、peekFirst() | 查询表头元素 | E |
peekLast() | 查询表尾元素 | E |
iterator() | 迭代器,用于循环遍历(每个集合都有对应的迭代器对象iterator) | Iterator |
和List一样,会用即可
public static void main(String[] args) {
LinkedList<String> list = new LinkedList();//相当于链表
list.add("123");
list.add("234");
list.add(1,"aaa");//下标不能越界,找1下标是效率低,加入元素是效率高的
list.addFirst("常");//特有方法,头插法,0下标位置插入
list.addLast("哲");//特有方法,尾插法,size-1下标位置插入
list.add("456");
System.out.println(list);
System.out.println(list.getFirst());//获取头结点
System.out.println(list.getLast());//获取尾结点
list.addLast("1");//没返回值,常用,因为语义化能看懂
System.out.println(list.offerLast("1"));//返回boolean,很少用
list.removeFirst();//链表空抛异常
list.pollFirst();//链表空返回null
System.out.println("===============================");
for(int i = 0;i < list.size();i++){
System.out.println();
}
}
源码分析(了解)
public class LinkedList<E>{//E是一个泛型,具体的类型要在实例化的时候才会最终确定
transient int size = 0;//集合中元素的数量
//Node的内部类
private static class Node<E> {
E item;//当前元素
Node<E> next;//指向下一个元素地址
Node<E> prev;//上一个元素地址
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
transient Node<E> first;//链表的首节点
transient Node<E> last;//链表的尾节点
//空构造器:
public LinkedList() {
}
//添加元素操作:
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {//添加的元素e
final Node<E> l = last;//将链表中的last节点给l 如果是第一个元素的话 l为null
//将元素封装为一个Node具体的对象:
final Node<E> newNode = new Node<>(l, e, null);
//将链表的last节点指向新的创建的对象:
last = newNode;
if (l == null)//如果添加的是第一个节点
first = newNode;//将链表的first节点指向为新节点
else//如果添加的不是第一个节点
l.next = newNode;//将l的下一个指向为新的节点
size++;//集合中元素数量加1操作
modCount++;
}
//获取集合中元素数量
public int size() {
return size;
}
//通过索引得到元素:
public E get(int index) {
checkElementIndex(index);//健壮性考虑
return node(index).item;
}
Node<E> node(int index) {
//如果index在链表的前半段,那么从前往后找
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {//如果index在链表的后半段,那么从后往前找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
}
(无下标,无序,数据不可重复(去重))
通过实现类创建对象:Set set = new HashSet();//不能用子类特有的方法
Set对象的方法(子类通用) | 说明 | 返回值 |
---|---|---|
add(E e) | 向集合添加元素e,若指定集合元素改变了则返回true | boolean |
addAll(Collection<? extends E> c) | 把集合C的元素全添加到集合中,若指定集合元素改变返回true | boolean |
clear() | 清空所有集合元素 | void |
contains(Object o) | 判断指定集合是否包含对象o | boolean |
containsAll(Collection<?> c) | 判断指定集合是否包含集合c的所有元素 | boolean |
isEmpty() | 判断指定集合的元素大小是否为0 | boolean |
remove(Object o) | 删除集合中的元素对象o,若有多个o元素,则只删除第一个元素 | boolean |
removeAll(Collection<?> c) | 删除指定集合包含集合c的元素,多个相同元素全删除 | boolean |
retainAll(Collection<?> c) | 从指定集合中保留包含集合c的元素,其他元素则删除 | boolean |
size() | 集合的元素个数 | int |
toArray(T[] a) | 将集合转换为T类型的数组 | T[] |
equals(Collection<?> c) | 比较两个集合中内容是否一样 | boolean |
iterator() | 迭代器,用于循环遍历(每个集合都有对应的迭代器对象iterator) | Iterator |
HashSet遍历方式[HashSet遍历方式]和TreeSet遍历方式一样[TreeSet遍历方式]
Set接口的遍历方式 | 使用说明: |
---|---|
Iterator迭代器方式 | Iterator iterator = Set对象.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } |
省内存的迭代器方式 | for(Iterator<类型> it = Set对象.iterator();it.hasNext(); ){ System.out.println(it.next()); } |
增强for循环,底层也是迭代器实现的 | for(类型 变量名 : Set对象){ System.out.println(变量名); } |
看下面代码,理解去重即可
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("3");
set.add("2");
set.add("1");
set.add("5");
set.add("aaa");
set.add("5");//相同数据不会添加进去,直接忽略
System.out.println(set.size());
set.remove("2");//返回boolean,没有根据下标删除的方法
System.out.println(set);
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
/*
【Set接口】(位置无序(不能下标找值了),数据不可重复)(去重的功能)
都是Collection的方法
*/
一般情况下:调用equals()方法是用来比较两个对象之间属性值是否相同,相同返回true,不同返回false
所以比较对象的时候我们要重写equals()方法
? 源码:public native int hashCode();
? 介绍:底层采用哈希算法,将程序中对象的内存地址转换成hash值。
一般情况下,重写equals()后,也要重写hashCode()方法
首先在add()方法中获得要添加的元素的值
hashCode值不同则认为是不同的对象,直接添加入集合
hashCode值相同(因为hashCode有可能是多个数相加,所以值还是可能一致的),通过equals()进行比较
比较结果为false不是相同对象,进行存储
比较结果为true则是相同对象,不进行存储
如果使用HashSet存储系统类型对象,可直接使用
如果使用HashSet存储自定义类型对象,需要重写对应类的hashCode()和equals()方法
快捷键:alt+insert
创建对象:HashSet<引用数据类型> set = new HashSet<>();
HashSet遍历方式和TreeSet遍历方式一样,看上面Set遍历
看下面代码重写hashCode()和equals()解析
import java.util.Objects;
public class Student{//比较器接口,TreeSet用的
private String id;
private String name;
private int age;
public Student(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public Student() {
}
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 String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
//重写该类的hashCode()和equals()方法,实现相同的人的去重(快捷键alt+insert)
@Override
public 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 && Objects.equals(id, student.id) && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
}
测试类:
import java.util.HashSet;
public class Demo7 {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
Student stu = new Student("0001","张三",20);
// set.add(new Student("0001","张三",20));//没重写时相同的人不会去重,因为new出的内存地址不同
// set.add(new Student("0001","张三",20));
set.add(stu);
set.add(stu);//去重
System.out.println(set.size());
set.add(new Student("0002","李四",22));
set.add(new Student("0002","李四",22));//相同成功去重,在Student类下重写hashCode()和equals()方法了
for (Student i: set) {
System.out.println("姓名:"+i.getName());
}
}
}
有序
、数据不可重复(去重))TreeSet底层Map实现,去重和排序是比较器实现
TreeSet底层排序的二叉树的遍历是按照升序的结果出现的,这个升序是靠中序遍历得到的,相等不存,小存左,大存右
当TreeSet集合调用add()方法,比较器compareTo()会对添加进来的元素进行比较
? 返回值为0,不允许添加
? 返回值为正数,添加至右子树
? 返回值为负数,添加至左子树
二期项目需要对某个自定义临时进行比较,意味着要改源码,可通过外部比较器
? 比较器:为了服务TreeSet,目的是去重和比较
? 内部比较器:直接让自定义类实现Comparable接口的compareTo()方法
? 外部比较器:优先级高于内部比较器
,独立于自定义类的,需创建新的类实现Comparator<自定义类>接口的compare(对象1,对象2)方法
如果使用TreeSet存储系统类型对象,可以直接使用
如果使用TreeSet存储自定义类型对象,需要该类实现Comparable接口的compareTo(对象)方法
或创建新的类实现Comparator<自定义类>接口的compare(对象1,对象2)方法
创建对象:TreeSet<引用数据类型> set = new TreeSet<>();
TreeSet遍历方式和HashSet遍历方式一样,看上面Set遍历
内部比较器写法:要会编写Comparable的compareTo方法实现排序
import java.util.TreeSet;
public class Demo8 {
public static void main(String[] args) {
TreeSet<Student> set = new TreeSet<>();//没有下标,数据不可重复,有排序功能
//set.add(new Student("0001","张三",20));//会报错,提示Student类不可转换成Comparable比较器,要在Student类实现接口的compareTo方法
set.add(new Student("0001","张三",20));
set.add(new Student("0001","张三",20));
set.add(new Student("0002","李四",25));
set.add(new Student("0003","王五",18));
set.add(new Student("0004","老六",30));
for (Student i: set) {
System.out.println("姓名:"+i.getName()+"\t年龄:"+ i.getAge());
}
String str = new String("abcdefg");
String str1 = new String("bbc");
System.out.println(str.compareTo(str1));
}
}
}
import java.util.Objects;
public class Student implements Comparable{//比较器接口,TreeSet用的
private String id;
private String name;
private int age;
public Student(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public Student() {
}
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 String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public int compareTo(Object o) {
//要自己写,比较此对象与指定对象的顺序,返回0相等,返回正数放在右子树,返回负数放在左子树
//TreeSet底层的二叉树的遍历是按照升序的结果出现的,这个升序是靠中序遍历得到的,相等不存,小存左,大存右
if (this == o){//地址相同
return 0;
}
if (o instanceof Student){//判断o是不是Student类型
Student student = (Student) o;//向下转型
if (this.id.equals(student.id)){//学生ID相同
return 0;
}else if(this.age > student.age){//按年龄排序
return 1;//放在右子树
}else {
return -1;//放在左子树
}
}
return 0;
}
}
外部比较器写法:要会编写Comparator的compare方法实现排序
public class Demo8 {
public static void main(String[] args) {
TreeSet<Student> set1 = new TreeSet<>(new StudentCmp());//外部比较器,注意这里
set1.add(new Student("0001","张三",20));
set1.add(new Student("0001","张三",20));
set1.add(new Student("0002","李四",25));
set1.add(new Student("1111","王五",18));
set1.add(new Student("0004","老六",18));
for (Student i: set1) {
System.out.println("ID:"+i.getId()+"\t姓名:"+i.getName()+"\t年龄:"+ i.getAge());
}
}
}
public class Student{
private String id;
private String name;
private int age;
public Student(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public Student() {
}
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 String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
import java.util.Comparator;
public class StudentCmp implements Comparator<Student> {
public int compare(Student o1, Student o2) {
if (o1.getId().equals(o2.getId())){//学号相等返回0,相等
return 0;
}else if(o1.getAge() > o2.getAge()){//按年龄排序,从大到小,当前对象o1大于o2就把o1放左子树先输出
return -1;//放在左子树
}else if(o1.getAge() < o2.getAge()){
return 1;//放在右子树
}else {//年龄相等比较ID小的放前
return o1.getId().compareTo(o2.getId());//ID进行比较
}
}
}
通过实现类创建对象:Map map = new HashMap();//不能用子类特有的方法
key一般放基本数据类型的包装类,value存放各种引用数据类型
key在TreeMap是有序的,HashMap是无序的,数据不可重复,value是无序的,数据可重复
HashMap、TreeMap常用方法 | 说明 | 返回值 | 用法 |
---|---|---|---|
put(Object key,Object value) | 将指定key-value添加到(或修改)当前map对象中 | Object | 添加 |
clear() | 清空当前map中的所有数据 | void | 清除 |
remove(Object key) | 移除指定key的键值对,并返回value | Object | 删除 |
get(Object key) | 获取指定key对应的value | Object | 查询 |
size() | 返回map中key-value对的个数 | int | 长度 |
isEmpty() | 判断当前map是否为空 | boolean | 判空 |
equals(Object obj) | 判断当前map和参数对象obj是否相等 | boolean | 判等 |
putAll(Map m) | 将m中的所有key-value对存放到当前map中 | void | 拷贝 |
containsKey(Object key) | 是否包含指定的key | boolean | 找key |
containsValue(Object value) | 是否包含指定的value | boolean | 找value |
keySet() | 返回所有key构成的Set集合 | Set | 遍历 |
values() | 返回所有value构成的Collection集合 | Collection | 遍历 |
entrySet() | 返回所有key-value对构成的Set集 | Set | 遍历 |
HashMap、TreeMap的三种遍历方式(重点,必须背下来)
由于Map没有迭代器,遍历需要通过下面三个方法生成Set和Collection集合进行遍历
同样可用、省内存的迭代器方式和增强for循环
方法 | 说明 | 使用方法:需创建HashMap或TreeMap对象设置泛型后 |
---|---|---|
keySet() | 获得map集合中,所有的key | Set<key类型> keySet = 对象.keySet();//得到所有key的set集合 Iterator<key类型> iterator = keySet.iterator(); while (iterator.hasNext()){ key类型 next = iterator.next(); System.out.println(“key:”+next+“value:”+对象.get(next)); } |
values() | 获得map集合中,所有的value | Collection<value类型> values = map.values(); Iterator<value类型> iterator = values.iterator(); while (iterator.hasNext()){ value类型 next = iterator.next(); System.out.println(“value的值:”+next); } |
entrySet() | 获得map集合中,key+value | Set<Map.Entry<key类型, value类型>> entries = map.entrySet(); Iterator<Map.Entry<key类型, value类型>> iterator = entries.iterator(); while (iterator.hasNext()){ Map.Entry<key类型, value类型> next = iterator.next(); System.out.println(“key:”+next.getKey()+“value:”+next.getValue()); } |
其他方法遍历 | 代码: |
---|---|
Iterator迭代器 | 看上面 |
迭代器省内存 | for(Iterator<key类型> it = map.keySet().iterator();it.hasNext(); ){ key类型 next = it.next(); System.out.println(“key:”+next+“value:”+map.get(next)); } |
for(Iterator<value类型> it = map.values().iterator();it.hasNext(); ){ value类型 next = it.next(); System.out.println(“value:”+next); } | |
for(Iterator<Map.Entry<key类型, value类型>> it = map.entrySet().iterator();it.hasNext(); ){ Map.Entry<key类型, value类型> next = it.next(); System.out.println(“key:”+next.getKey()+“value:”+next.getValue()); } | |
forEach方式快捷键.for | for (key类型 key1 : map.keySet()) { System.out.println(“key:”+key1+“value:”+map.get(key1)); } |
for (value类型 value1 : map.values()) { System.out.println(“value:”+value1); } | |
for (Map.Entry<key类型, value类型> next : map.entrySet()) { System.out.println(“key:”+next.getKey()+“value:”+next.getValue()); } | |
for循环 | Collection<value类型> values = map.values(); Object[] array = values.toArray(); for (int i = 0; i < array.length; i++) { System.out.println("value:"array[i]); } |
可通过map对象.上面的方法.var快捷生成Set和Collection对象
再在循环内获取集合中的单个对象都可通过IDEA的.var快捷生成对象
可看下面代码理解`
创建对象:HashMap<key类型, value类型> map = new HashMap<>();
import java.util.*;
public class Demo9 {
public static void main(String[] args) {
HashMap<Integer,String> map = new HashMap<>();
map.put(1,"张三");
map.put(1,"李四");//会覆盖原来key对应的数据
map.put(2,"王五");
map.put(3,"老六");
map.put(9,"小七");
map.put(0,"大八");
System.out.println(map.remove(4));
System.out.println(map);
//由于没有迭代器,使用3种遍历Map集合的方式:【面试常考】【迭代器还可细分10种】
//【方式一】
Set<Integer> keySet = map.keySet();//得到所有key的set集合
Iterator<Integer> iterator = keySet.iterator();
while (iterator.hasNext()){
Integer next = iterator.next();
System.out.println("key的值是:"+next+"\tvalue值是:"+map.get(next));
}
//【方式二】
Collection<String> values = map.values();
Iterator<String> iterator1 = values.iterator();
while (iterator1.hasNext()){
String next = iterator1.next();
System.out.println("【方式三】value的值:"+next);
}
//【方式三】
Set<Map.Entry<Integer, String>> entries = map.entrySet();
Iterator<Map.Entry<Integer, String>> iterator2 = entries.iterator();
while (iterator2.hasNext()){
Map.Entry<Integer, String> next = iterator2.next();
System.out.println("【方式三】key的值:"+next.getKey()+"\tvalue的值:"+next.getValue());
}
//【自己扩展方法】
System.out.println("=============================");
//省内存迭代器方式
for(Iterator<Integer> it = map.keySet().iterator();it.hasNext();){
Integer next = it.next();
System.out.println("key:"+next+"value:"+map.get(next));
}
for(Iterator<String> it = map.values().iterator();it.hasNext();){
String next = it.next();
System.out.println("value:"+next);
}
for(Iterator<Map.Entry<Integer, String>> it = map.entrySet().iterator();it.hasNext();){
Map.Entry<Integer, String> next = it.next();
System.out.println("key:"+next.getKey()+"value:"+next.getValue());
}
//for循环,只能map.values()
Collection<String> values1 = map.values();
Object[] array = values1.toArray();
for (int i = 0; i < array.length; i++) {
System.out.println("value:"+array[i]);
}
//foreach方式
for (Integer key : map.keySet()) {
System.out.println("key:"+key+"value:"+map.get(key));
}
for (String value : map.values()) {
System.out.println("value:"+value);
}
for (Map.Entry<Integer, String> next : map.entrySet()) {
System.out.println("key:"+next.getKey()+"value:"+next.getValue());
}
}
}
创建对象:TreeMap<key类型, value类型> map = new TreeMap<>();
(1)内部比较器:
import java.util.Map;
import java.util.TreeMap;
public class Test03 {
public static void main(String[] args) {
Map<Student,Integer> map = new TreeMap<>();
map.put(new Student(19,"blili",170.5),1001);
map.put(new Student(18,"blili",150.5),1003);
map.put(new Student(19,"alili",180.5),1023);
map.put(new Student(17,"clili",140.5),1671);
map.put(new Student(10,"dlili",160.5),1891);
System.out.println(map);
System.out.println(map.size());
}
}
public class Student implements Comparable<Student>{
private int age;
private String name;
private double height;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public Student(int age, String name, double height) {
this.age = age;
this.name = name;
this.height = height;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
", height=" + height +
'}';
}
@Override
public int compareTo(Student o) {
/* return this.getAge()-o.getAge();*/
return this.getName().compareTo(o.getName());
}
}
(2)外部比较器:
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
public class Test03 {
public static void main(String[] args) {
Map<Student,Integer> map = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return ((Double)(o1.getHeight())).compareTo((Double)(o2.getHeight()));
}
});
map.put(new Student(19,"blili",170.5),1001);
map.put(new Student(18,"blili",150.5),1003);
map.put(new Student(19,"alili",180.5),1023);
map.put(new Student(17,"clili",140.5),1671);
map.put(new Student(10,"dlili",160.5),1891);
System.out.println(map);
System.out.println(map.size());
}
}
import java.util.ArrayList;
import java.util.Collections;
public class Test01 {
//这是main方法,程序的入口
public static void main(String[] args) {
//Collections不支持创建对象,因为构造器私有化了
/*Collections cols = new Collections();*/
//里面的属性和方法都是被static修饰,我们可以直接用类名.去调用即可:
//常用方法:
//addAll:
ArrayList<String> list = new ArrayList<>();
list.add("cc");
list.add("bb");
list.add("aa");
Collections.addAll(list,"ee","dd","ff");
Collections.addAll(list,new String[]{"gg","oo","pp"});
System.out.println(list);
//binarySearch必须在有序的集合中查找:--》排序:
Collections.sort(list);//sort提供的是升序排列
System.out.println(list);
//binarySearch
System.out.println(Collections.binarySearch(list, "cc"));
//copy:替换方法
ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list2,"tt","ss");
Collections.copy(list,list2);//将list2的内容替换到list上去
System.out.println(list);
System.out.println(list2);
//fill 填充
Collections.fill(list2,"yyy");
System.out.println(list2);
}
}
方法名 | 说明 | 返回值 |
---|---|---|
Collections.addAll(集合,[数组][.数据]…) | 集合后添加数据 | boolean |
Collections.sort(集合) | 集合升序排序 | void |
Collections.binarySearch(集合,数据) | 二分查找,返回下标 | int |
Collections.copy(集合1,集合2) | 集合2对应位置的内容替换到集合1对应位置中,其他不变 | void |
fill(集合,数据) | 整个集合填充成指定内容 | void |
【1】在没有通配符的时候:
下面的a方法,相当于方法的重复定义,报错
public class Test {
/*public void a(List<Object> list){
}
public void a(List<String> list){
}
public void a(List<Integer> list){
}*/
}
【2】引入通配符:
public class Demo {
//这是main方法,程序的入口
public static void main(String[] args) {
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<Integer> list3 = new ArrayList<>();
List<?> list = null;
list = list1;
list = list2;
list = list3;
}
}
发现: A 和 B是子类父类的关系,G<A>和G<B>不存在子类父类关系,是并列的
加入通配符?后,G<?>就变成了 G<A>和G<B>的父类
【3】使用通配符:
import java.util.ArrayList;
import java.util.List;
public class Test {
/*public void a(List<Object> list){
}
public void a(List<String> list){
}
public void a(List<Integer> list){
}*/
public void a(List<?> list){
//内部遍历的时候用Object即可,不用?
for(Object a:list){
System.out.println(a);
}
}
}
class T{
//这是main方法,程序的入口
public static void main(String[] args) {
Test t = new Test();
t.a(new ArrayList<Integer>());
t.a(new ArrayList<String>());
t.a(new ArrayList<Object>());
}
}
E.使用通配符后的细节
public class Test {
public void a(List<?> list){
//1.遍历:
for(Object a:list){
System.out.println(a);
}
//2.数据的写入操作 :
//list.add("abc");-->出错,不能随意的添加数据
list.add(null);
//3.数据的读取操作:
Object s = list.get(0);
}
}
class T{
//这是main方法,程序的入口
public static void main(String[] args) {
Test t = new Test();
t.a(new ArrayList<Integer>());
t.a(new ArrayList<String>());
t.a(new ArrayList<Object>());
}
}
F.泛型受限,所以很少用了
import java.util.ArrayList;
import java.util.List;
public class Test {
//这是main方法,程序的入口
public static void main(String[] args) {
//a,b,c三个集合是并列的关系:
List<Object> a = new ArrayList<>();
List<Person> b = new ArrayList<>();
List<Student> c = new ArrayList<>();
/*开始使用泛型受限:泛型的上限
List<? extends Person>:
就相当于:
List<? extends Person>是List<Person>的父类,是List<Person的子类>的父类
*/
List<? extends Person> list1 = null;
/*list1 = a;
list1 = b;
list1 = c;*/
/*开始使用泛型受限:泛型的下限
List<? super Person>
就相当于:
List<? super Person>是List<Person>的父类,是List<Person的父类>的父类
*/
List<? super Person> list2 = null;
list2 = a;
list2 = b;
list3 = c;
}
}
Collection 接口存储一组不唯一,无序的对象
List 接口存储一组不唯一,有序(插入顺序)的对象
Set 接口存储一组唯一,无序的对象
Map接口存储一组键值对象,提供key到value的映射。Key无序,唯一。value不要求有序,允许重复。(如果只使用key存储,而不使用value,那就是Set)
Vector和ArrayList的区别和联系
实现原理相同,功能相同,都是长度可变的
数组结构,很多情况下可以互用
两者的主要区别如下
两者都实现了List接口,都具有List中元素有序
、不唯一的特点。
ArrayList实现了长度可变的数组,在内存中分配连续空间。遍历元素
和随机访问元素
的效率比较高;
LinkedList采用链表存储方式。插入、删除元素时效率比较高
数组不是面向对象的,存在明显的缺陷,集合完全弥补了数组的一些缺点,比数组更灵活更实用,可大大提高软件的开发效率而且不同的集合框架类可适用于不同场合。具体如下: