文章目录
一、集合
- 概念:对象的容器,定义了对多个对象进行操作的常用方法。实现了类似数组的功能。
- 和数组的区别:
- 数组长度固定,集合长度不固定;
- 数组可以存储基本类型和引用类型,集合只能存储引用类型;
- 位置:java.util.*;
二、Collection体系集合
- 该体系结构的根接口,代表一组对象,称为”集合“。
- List接口的特点:
- 有序、有下标、元素可重复
- Set接口的特点:
- 无序、无下标、元素不能重复
三、Collection父接口
- 特点:代表一组任意类型的对象,无序,无下标、不能重复。
- 方法:
- boolean add(Object obj) //添加一个对象。
- boolean addAll(Collection c) //将一个集合中的所有对象添加到此集合中。
- void clear()//清空此集合中的所有对象。
- boolean contains(Object o)//检查此集合中是否包含0对象
- boolean equals(Object o) //比较此集合是否与指定对象相等
- boolean isEmpty()//判断此集合是否为空
- boolean remove(Object o) //在此集合中移除对象
- int size()//返回此集合中的元素个数。
- Object[] toArray()//将此集合转换成数组。
- Iterator iterator() //迭代器 重点
package com.set.collection;
/*
* Collection接口的使用
* (1)添加远胜于
* (2)删除元素
* (3)遍历元素
* (4)判断
* */
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo01 {
public static void main(String[] args) {
//创建集合
Collection collection = new ArrayList();
//(1)添加远胜于
collection.add("苹果");
collection.add("西瓜");
collection.add("榴莲");
System.out.println("元素个数"+collection.size());
System.out.println(collection.toString());
//(2)删除元素
// collection.remove("榴莲");
// collection.clear(); //清空
System.out.println(collection.size());
//(3)遍历元素[重点!!!]
//第一种方式
System.out.println("---------第一种方式:增强for---------");
for (Object object: collection){
System.out.println(object);
}
//第二种饭方式 迭代器 专门用来遍历集合的方法
System.out.println("---------第一种方式:iterator---------");
//hasNext() 有没有下一个元素
//next() 获取下一个元素
// remove() 删除当前元素
Iterator it = collection.iterator();
while (it.hasNext()){
String s = (String)it.next();
System.out.println(s);
//迭代过程中不允许collection删除方法: collection.remove(s)
// 否则会出现异常ConcurrentModificationException(并发修改异常)
//collection.remove(s);
it.remove();
}
System.out.println("元素个数:"+collection.size());
//(4)判断
System.out.println(collection.contains("西瓜"));
System.out.println(collection.isEmpty());
}
}
package com.set.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
//Collection的使用二 Student类
public class Demo02 {
public static void main(String[] args) {
//1、创建Collection对象
Collection collection = new ArrayList();
//2、创建Student对象
Student s1 = new Student("张三",18);
Student s2 = new Student("李四",19);
Student s3 = new Student("王五",20);
//3、添加数据
collection.add(s1);
collection.add(s2);
collection.add(s3);
System.out.println("元素个数"+collection.size());
System.out.println(collection.toString());
//4、删除
collection.remove(s1);
//collection.remove(new Student("王五",20));//并不能删除集合中的s3,用为new 地址不同
//collection.clear();//清空
//集合中存的是对象的地址 清空后这几个对象并没有消失,删除的是地址,对象还在堆中
System.out.println("删除之后:"+collection.size());
//5、遍历
System.out.println("------一、增强for来遍历--------");
for (Object obj:collection) {
Student stu1 = (Student) obj;
System.out.println(stu1.toString());
}
System.out.println("------二、迭代器遍历--------");
Iterator it = collection.iterator();
while (it.hasNext()){
Student s = (Student) it.next();//强制转换
System.out.println(s.toString());
}
//5、判断
System.out.println("------判断--------");
System.out.println(collection.contains(s2));
System.out.println(collection.isEmpty());
}
}
四、List子接口
- 特点:有序、有下标、元素可以重复。
- 方法(具有父接口Collection的所有方法)
- void add( int index, Object o ) //在index位置插入对象。
- boolean addAll(int index , Collection c ) //将一个集合的元素添加到此集合中的index位置。
- Object get( int index) //返回集合中指定位置的元素
- List subList(int fromIndex,int toindex)v //返回fromIndex和toIndex之间的集合元素。
package com.set.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
//List子接口的使用
/*
* 特点:有序、有下标、可以重复
* */
public class Demo01 {
public static void main(String[] args) {
//先创建一个集合
List list = new ArrayList<>();
//1、添加元素
list.add("小米");
list.add("苹果");
list.add("华为");
System.out.println("元素个数:"+list.size());
System.out.println(list.toString());
//2、删除元素
//list.remove("苹果");
//list.remove(0);
System.out.println(list.size());
System.out.println(list.toString());
//3、遍历【重点!!!】
//使用for遍历
System.out.println("-------3、1for遍历-------");
for (int i = 0; i < list.size() ; i++) {
System.out.println(list.get(i));
}
System.out.println("-------3、2增强for遍历-------");
for (Object obj:list) {
System.out.println(obj);
}
System.out.println("-------3、3迭代器遍历-------");
Iterator it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
System.out.println("-------3、4列表迭代器-------");
ListIterator lit = list.listIterator();
System.out.println("-----------3、4、1列表迭代器从前往后-----------");
while (lit.hasNext()){
System.out.println(lit.nextIndex()+":"+lit.next());
}
System.out.println("-----------3、4、1列表迭代器从后往前-----------");
/*while (lit.hasPrevious()){
System.out.println(lit.previousIndex()+":"+lit.previous());
}*/
//4、判断
System.out.println("-------3、5判断-------");
System.out.println(list.contains("苹果"));
System.out.println(list.isEmpty());
//5、获取位置
System.out.println(list.indexOf("华为"));
}
}
五、List实现类
-
ArrayList(重点)
- 数组结构实现,查询快、增删慢;
- JDK1.2版本,运行效率快、线程不安全。
-
Vector:
- 数组结构实现,查询快、增删慢;
- JDK1.0版本,运行效率慢、线程安全。
-
LinkedList:
- 链表结构实现,增删快,查询慢。
5、1 ArrayList
- 存储结构:数组 查找遍历速度快,增删慢
- 存储地址是连续的
- 创建集合
//创建集合
ArrayList arraylist = new ArrayList<>();
- 方法和List的方法基本一样
//1、添加元素
Student s1 = new Student("张三",18);
Student s2 = new Student("李四",19);
Student s3 = new Student("王五",20);
Student s4 = new Student("王麻子",21);
arraylist.add(s1);
arraylist.add(s2);
arraylist.add(s3);
arraylist.add(s4);
System.out.println("-------1、添加元素--------");
System.out.println("元素个数:"+arraylist.size());
System.out.println(arraylist.toString());
//2、删除元素
System.out.println("-------2、删除元素--------");
//arraylist.remove(s1);
//arraylist.remove(new Student("李四",19));//equals(this == obj)方 法比较的是地址
System.out.println("删除之后:"+arraylist.size());
System.out.println(arraylist.toString());
//3、遍历元素【重点】
System.out.println("-------3、遍历元素--------");
//3、1迭代器遍历
System.out.println("-------3、1使用迭代器iterator--------");
Iterator it = arraylist.iterator();
while (it.hasNext()){
Student student1 = (Student) it.next();
System.out.println(student1.toString());
}
//ListIterator列表迭代器
System.out.println("-------3、2使用列表迭代器LisIterator--------");
System.out.println("-------从前往后-------");
ListIterator lit = arraylist.listIterator();
while (lit.hasNext()){
Student student2 = (Student) lit.next();
System.out.println(student2.toString());
}
System.out.println("-------从后往前-------");
while(lit.hasPrevious()){
Student student3 = (Student) lit.previous();
System.out.println(student3.toString());
}
//4、判断
System.out.println("-------4、判断元素--------");
System.out.println(arraylist.contains(new Student("张三",18)));
System.out.println(arraylist.isEmpty());
//3、查找
System.out.println("-------5、查找元素--------");
System.out.println(arraylist.indexOf(s1));
System.out.println(arraylist.indexOf(new Student("王麻子",21)));
- equals方法重写
@Override
public boolean equals(Object obj) {
//1、比较是不是同一个引用
if(this == obj){
return true;
}
//2、判断是否为空
if(obj == null){
return false;
}
//3、判断是不是Student类型
if(obj instanceof Student){
Student student = (Student) obj;
//4、比较属性
if (Objects.equals(this.name, student.getName()) &&this.age==student.getAge()){
return true;
}
}
return false;
}
5、2 ArrayList源码分析
-
默认容量大小:int DEFAULT_CAPACITY = 10;
- 注意:如果没有像集合中添加数据,容量为0,添加任意一个元素之后容量10;
- 扩容:每次扩容大小是原来的1.5倍;
-
存放元素的数组:Object[] elementData;
-
实际元素个数:size;
-
add():添加元素
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
//数组扩容核心 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
5、3 Vector使用
-
Vector:
- 数组结构实现,查询快、增删慢;
- JDK1.0版本,运行效率慢、线程安全。
-
创建Vector集合
//创建集合 Vector vector = new Vector<>(); //1、添加元素 System.out.println("------1、添加元素------"); vector.add("草莓"); vector.add("芒果"); vector.add("苹果"); //2、遍历 Enumeration en = vector.elements(); while (en.hasMoreElements()){ String o = (String) en.nextElement(); System.out.println(o); } //其他方法和ArrayList的方法类似
5、4 LinkedList使用
-
LinkedList
- 双向链表 增删快 查询慢
- 存储地址不是连续的
-
创建LinkedList以及常用方法
-
//创建集合 LinkedList<Object> linkedList = new LinkedList<>(); System.out.println(linkedList.getFirst());//获取第一个元素 System.out.println(linkedList.getLast());//获取最后一个元素
-
-
其他方法和ArrayList类似。
5、5泛型
5、5、1泛型类
-
Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作为参数传递。
-
注意:
-
泛型只能传引用类型。
-
不同泛型对象之间不能相互赋值
//Demo01<String> stringDemo011 = integerDemo01; //不同泛型对象不能相互复制
-
-
常见形式有泛型类、泛型接口、泛型方法。
-
语法:
- <T,…> T称为类型占位符,表示一种引用类型。
-
好处:
(1)提高代码的重用性。
(2)防止类型转换异常,提高代码的安全性。
package com.set.Generic;
//泛型类
//语法 类名<T> T是类型占位符,表示是一种引用类型,可以写多个,用逗号隔开
public class Demo01<T> {
//使用泛型创建变量
//1、创建变量
T t;
//2、作为方法的参数
public void show(T t){
// T t1 = new T(); 不能这样直接实例化 因为不能保证传过来的类型构造方法一定能用
System.out.println(t);
}
//3、使用泛型作为方法的返回值
public T getT(){
return t;
}
}
package com.set.Generic;
public class Application {
public static void main(String[] args) {
//泛型类创建对象
//注意:1、泛型只能是引用类型 2、不同泛型对象之间不能相互赋值
Demo01<String> stringDemo01 = new Demo01<>();
stringDemo01.t = "hello";
stringDemo01.show("大家好,学个屁的Java,别学了");
String result = stringDemo01.getT();
System.out.println(result);
Demo01<Integer> integerDemo01 = new Demo01<>();
integerDemo01.t = 20;
integerDemo01.show(30);
System.out.println(integerDemo01.getT());
//Demo01<String> stringDemo011 = integerDemo01; //不同泛型对象不能相互复制
}
}
5、5、2泛型接口
- 注意:泛型接口类不能创建泛型常量
- 语法 :接口名:
-
创建泛型接口:
package com.set.Generic.Myinterfece; //泛型接口 //语法 接口名:<T> //注意:不能创建泛型常量 public interface Demo01 <T>{ String name = "张三"; T server(T t); }
-
实现类第一种 第一种实现方式(实例化之前确定泛型类的引用类型)
package com.set.Generic.Myinterfece; public class Demo01Impl implements Demo01<String> { @Override public String server(String t) { System.out.println(t); return t; } }
-
实现类第二种 第二种实现方式(实例化的时确定泛型类的引用类型)
package com.set.Generic.Myinterfece; public class Demo01Impl2<T> implements Demo01<T> { @Override public T server(T t) { System.out.println(t); return t; } }
-
启动类
package com.set.Generic.Myinterfece; public class Application { public static void main(String[] args) { //第一种实现方式(实例化之前确定泛型类的引用类型) Demo01Impl demo01 = new Demo01Impl(); demo01.server("hello"); //第二种实现方式(实例化的时确定泛型类的引用类型) Demo01Impl2<Integer> integerDemo01Impl2 = new Demo01Impl2<>(); integerDemo01Impl2.server(1000); } }
5、5、3 泛型方法
5、5、3 、1 无界泛型方法
-
下面是定义泛型方法的规则:
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的 )。
- 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)。
- 语法:放在方法返回值的前面 T 方法返回值类型 方法名(T t)
-
java 中泛型标记符:
-
E - Element (在集合中使用,因为集合中存放的是元素)
-
T - Type(Java 类)
-
K - Key(键)
-
V - Value(值)
-
N - Number(数值类型)
-
? - 表示不确定的 java 类型
-
- <? extends T>和<? super T>的区别
- <? extends T>表示该通配符所代表的类型是T类型的子类
- <? super T>表示该通配符所代表的类型是T类型的父类
package com.set.Generic.Methods;
//泛型方法
//语法:放在方法返回值的前面 T 方法返回值类型 方法名(T t)
public class Demo01 {
//打印不同类型的数组
public <T> void arrayPrint(T[] inputArray){
for (T t:inputArray) {
System.out.print(t+",");
}
System.out.println();
}
public static void main(String[] args) {
Demo01 demo01 = new Demo01();
Integer[] integers ={1,2,4,5,6};
Double[] doubles = {1.2,3.4,5.6};
Character[] characters = {'h','e','l','l','o'};
System.out.println("--------整形数组-------");
demo01.arrayPrint(integers);
System.out.println("--------双精度数组-------");
demo01.arrayPrint(doubles);
System.out.println("--------字符数组-------");
demo01.arrayPrint(characters);
}
}
5、5、3 、2 有界泛型方法
- 可能有时候,你会想限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。
- 要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。
实例
- 下面的例子演示了"extends"如何使用在一般意义上的意思"extends"(类)或者"implements"(接口)。该例子中的泛型方法返回三个可比较对象的最大值。
package com.set.Generic.Methods;
//泛型方法 有界的类型参数
public class MaximumTest {
public <T extends Comparable<T>> T maximum(T t1,T t2,T t3){
T max = t1;
if(t2.compareTo(max)>0){
max = t2;
}
if(t3.compareTo(max)>0){
max = t3;
}
System.out.println("最大值为:"+max);
return max;
}
public static void main(String[] args) {
MaximumTest maximumTest = new MaximumTest();
maximumTest.maximum(4,3,8);
maximumTest.maximum(1.1,2.2,3.3);
maximumTest.maximum("java","c","php");
}
}
5、5、3 、3 类型通配符
- 类型通配符一般是使用 **?**代替具体的类型参数例如List<?>在逻辑上是 List, List, List,等所有List<具体的实参类型>的父类。
- 实例 因为getData()放法的参数是List<?>,所以stringgs,integers,doubles都可以作为这个方法的实参,这就是通配符的作用。
- 类型通配符上限通过形如List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。
- //demo01.getUperNumber(strings);//这里会报错,因为getUperNumber()放法中的参数已经定义了参数泛型上限为Number,所以泛型为String是不在这个范围内,所以会报错。
package com.set.Generic.Methods;
//实例
import java.util.ArrayList;
import java.util.List;
public class Demo01 {
//根据List的不同类型打印下标为0的元素
public void getData(List<?> data){
System.out.println("data:"+data.get(0));
}
public void getUperNumber(List<? extends Number> data){
System.out.println("data:"+data.get(0));
}
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
List<Double> doubles = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
strings.add("hello");
doubles.add(1.2);
integers.add(30);
Demo01 demo01 = new Demo01();
//demo01.getUperNumber(strings);//
demo01.getUperNumber(doubles);
demo01.getUperNumber(integers);
demo01.getData(strings);
}
}
5、5、4 泛型集合
- 概念:参数化类型、类型安全的集合,强制集合元素必须一致
- 特点:
- 编译时即可检查,而非运行是抛出异常
- 访问时,不必类型转换(拆箱)
- 不同泛型之间不能相互赋值,泛型不存在多态。
package com.set.Generic.GenericSet;
import com.set.Collection.Student;
import java.util.ArrayList;
import java.util.Iterator;
public class Demo01 {
public static void main(String[] args) {
ArrayList <String> arrayList = new ArrayList<String>();
arrayList.add("张三");
arrayList.add("李四");
arrayList.add("王五");
arrayList.add("王麻子");
for (String str:arrayList) {
System.out.println(str);
}
ArrayList <Student> arrayList2 = new ArrayList<Student>();
Student s1 = new Student("张三",18);
Student s2 = new Student("李四",19);
Student s3 = new Student("王五",20);
Student s4 = new Student("王麻子",21);
arrayList2.add(s1);
arrayList2.add(s2);
arrayList2.add(s3);
arrayList2.add(s4);
Iterator<Student> it = arrayList2.iterator();
while (it.hasNext()){
Student st = it.next();//不用强制转换
System.out.println(st);
}
}
}
六 、Set集合
- 特点:无序、无下标、元素不可重复。
- 方法:全部继承自Collection中的方法。
package com.Set.Demo01;
import java.lang.ref.SoftReference;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
//测试Set接口的使用
//特点:无序、无下标、元素不可重复。
public class Demo01 {
public static void main(String[] args) {
//创建集合
Set<String> set = new HashSet<>();
//1、添加数据
set.add("小米");
set.add("苹果");
set.add("vivo");
set.add("oppo");
System.out.println("元素个数:"+set.size());
System.out.println(set.toString());//无顺序
//2、删除元素
//set.remove("苹果");
//System.out.println("删除后:"+set.toString());
//3、遍历
//3、1 增强for
System.out.println("------增强for遍历------");
for (String str:set) {
System.out.println(set.toString());
}
//3、2迭代器便利
System.out.println("------迭代器遍历------");
Iterator<String> it = set.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//判断
System.out.println(set.contains("小米"));
System.out.println(set.isEmpty());
}
}
七、Set实现类
-
HashSet [重点]:
-
基于HshCode计算元素的存放位置。
-
当存入元素的哈希码相同时,会调用equals进行确认,如果为true,则拒绝后者存入。
-
-
TreeSet:
- 基于排列顺序实现元素不重复。
- 实现了SortedSet接口,对集合元素自动排序。
- 元素对象的类型必须实现Comparable接口,指定排序规则。
- 通过compareTo方法确定是否为重复元素
7、1 HashSet的使用
- HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
- HashSet 允许有 null 值。
- HashSet 是无序的,即不会记录插入的顺序。
- HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 您必须在多线程访问时显式同步对 HashSet 的并发访问。
- HashSet 实现了 Set 接口。
- 存储过程
- 根据hashcode计算保存的位置,如果此位置为空,则直接保存,不为空则执行第二部。
- 再执行equals方,如果equals方法为true,则认为是重复的,否则形成链表。
package com.Set.HashSet;
import java.util.HashSet;
import java.util.Iterator;
//HashSet的使用
//存储结构:数组+链表+红黑树
public class Demo01 {
public static void main(String[] args) {
//创建集合
HashSet<String> hashSet = new HashSet<>();
//添加元素
hashSet.add("张三");
hashSet.add("李四");
hashSet.add("王五");
hashSet.add("王麻子");
hashSet.add("王麻子");//重复的元素不会被添加
System.out.println("元素个数:"+hashSet.size());
System.out.println(hashSet.toString());
//删除元素
hashSet.remove("张三");
System.out.println("删除后:"+hashSet.size());
//遍历
System.out.println("-------增强for遍历-------");
for (String str:hashSet) {
System.out.println(str);
}
System.out.println("-------迭代器遍历-------");
Iterator<String> it = hashSet.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//判断
System.out.println(hashSet.contains("李四"));
System.out.println(hashSet.isEmpty());
}
}
package com.Set.HashSet;
import java.util.Objects;
public class Person {
private String name;
private int 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;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || this.getClass() != obj.getClass()) {
return false;
}
Person person = (Person) obj;
return Objects.equals(this.name, person.getName()) && this.age == person.getAge();
}
@Override
public int hashCode() {
//return Objects.hash(name, age);
int n1 = this.name.hashCode();
//(1) 31是一个质数,减少散列冲突(尽量是计算出的hashcode不冲突)
//(2) 31 提高执行效率 31*i = (i<<5)-i
int n2 = this.age+31;
return n1*n2;
}
}
package com.Set.HashSet;
import java.util.HashSet;
import java.util.Iterator;
//HashSet的使用
//存储结构:数组+链表+红黑树
/*
* (1)根据hashcode计算的保存位置,如果此位置为空,则直接把保存,如果不为空执行第二步
* (2)再执行equals方法,如果equals方法为true,则认为是重复的,否则,形成链表
* */
public class Demo02 {
public static void main(String[] args) {
//创建集合
HashSet<Person> persons = new HashSet<>();
//添加数据
Person p1 = new Person("张三",18);
Person p2 = new Person("李四",19);
Person p3 = new Person("王五",20);
Person p4 = new Person("王麻子",21);
//1、添加元素
persons.add(p1);
persons.add(p2);
persons.add(p3);
persons.add(p4);
persons.add(new Person("王麻子",21));//这里的是新的一个对象,地址不相同,所以能添加进去
//perosns.add(p4);//重复元素不能添加
System.out.println("元素个数:"+persons.size());
System.out.println(persons.toString());
//2、删除
// persons.remove(p1);
//persons.remove(new Person("王麻子",21));
// 现在能删除,没有重写hashCode和equals方法的时候是不可以的
System.out.println("删除之后:"+persons.size());
System.out.println(persons.toString());
//3、遍历
System.out.println("------增强for遍历-------");
for (Person perosn:persons) {
System.out.println(perosn.toString());
}
System.out.println("------迭代器遍历-------");
Iterator<Person> it = persons.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//4、判断
System.out.println(persons.contains(p1));
System.out.println(persons.isEmpty());
System.out.println(persons.contains(new Person("张三",18)));//true
}
}
7、2 TreeSet的使用
- TreeSet:
- 基于排列顺序实现元素不重复。
- 实现了SortedSet接口,对集合元素自动排序。
- 元素对象的类型必须实现Comparable接口,指定排序规则。
- 通过compareTo方法确定是否为重复元素
- 出现如例子类似的情况时候需要在Comparable实现类里重写compareTo方法,定义比较规则
例子 :
package com.Set.HashSet;
import java.util.Objects;
public class Person implements Comparable<Person> {
private String name;
private int 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;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || this.getClass() != obj.getClass()) {
return false;
}
Person person = (Person) obj;
return Objects.equals(this.name, person.getName()) && this.age == person.getAge();
}
@Override
public int hashCode() {
//return Objects.hash(name, age);
int n1 = this.name.hashCode();
//(1) 31是一个质数,减少散列冲突(尽量是计算出的hashcode不冲突)
//(2) 31 提高执行效率 31*i = (i<<5)-i
int n2 = this.age+31;
return n1*n2;
}
@Override
public int compareTo(Person person) {
int n1 = this.getName().compareTo(person.getName());
int n2 = this.age-person.getAge();
return n1==0?n2:n1;
//先按姓名比,再按年龄比
}
}
package com.Set.TreeSet;
//TreeSet的使用
//存储结构 红黑树
//要求:元素必须要实现Comparable接口 compareTo()方法的返回值为0 认为是重复元素
import com.Set.HashSet.Person;
import java.util.Iterator;
import java.util.TreeSet;
public class Demo02 {
public static void main(String[] args) {
//创建集合
TreeSet<Person> persons = new TreeSet<>();
Person p1 = new Person("zhangsan",18);
Person p2 = new Person("lisi",19);
Person p3 = new Person("wangwu",20);
Person p4 = new Person("wangmazi",21);
Person p5 = new Person("wangmazi",22);
//1、添加元素
persons.add(p1);
persons.add(p2);
persons.add(p3);
persons.add(p4);
persons.add(p5);
System.out.println("元素个数:"+persons.size());
System.out.println(persons.toString());
//2、删除
persons.remove(p1);
//persons.remove(new Person("lisi",19));
System.out.println("删除之后:"+persons.size());
System.out.println(persons.toString());
//3、遍历
System.out.println("------增强for------");
for (Person person:persons) {
System.out.println(person);
}
System.out.println("------迭代器------");
Iterator<Person> it = persons.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//4、判断
System.out.println(persons.contains(p2));
System.out.println(persons.isEmpty());
}
}
7、3 Comparator接口
-
TreeSet的使用
-
注意:
-
自定义比较规则和comparable中的compare方法和compareTo的返回值添加负号和不添加负号的区别、
-
如果是int类型添加负号是从大到小排序,否则是从小到大排序,入如果是String类型的比较,返回值添加负号是按字母表逆序排序,否则按正序排序。
TreeSet<String> strings = new TreeSet<>(new Comparator<String>() { @Override public int compare(String o1, String o2) { int n1 = o1.length()-o2.length();//按长度比较 int n2 = o1.compareTo(o2);//长度相等就按照字母表比较规则 return n1==0 ? n2:-n1;//n2大于0,按字母表正序,否则按逆序。n2大于0 按length从小到大,否则从大到小 } });
-
-
Comparator:实现定制比较(比较器)
//创建集合,并指定了比较规则 TreeSet<Person> persons = new TreeSet<>(new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { int n1 = o1.getAge()-o2.getAge(); int n2 = o1.getName().compareTo(o2.getName()); return n1==0? n2:n1;//如果年龄相等比较名字,如果年龄不相等则比较年龄 } });
-
Comparable:可比较的
package com.Set.TreeSet;
import com.Set.HashSet.Person;
import java.util.Comparator;
import java.util.TreeSet;
public class Demo03 {
public static void main(String[] args) {
//创建集合,并指定了比较规则
TreeSet<Person> persons = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
int n1 = o1.getAge()-o2.getAge();
int n2 = o1.getName().compareTo(o2.getName());
return n1==0? n2:n1;//如果年龄相等比较名字,如果年龄不相等则比较年龄
}
});
Person p1 = new Person("zhangsan",18);
Person p2 = new Person("lisi",19);
Person p3 = new Person("wangwu",20);
Person p4 = new Person("lisi",20);
//1、添加元素
persons.add(p1);
persons.add(p2);
persons.add(p3);
persons.add(p4);
System.out.println("元素个数:"+persons.size());
System.out.println(persons.toString());
}
}
7、4 TreeSet案例
- 通过TreeSet 实现字符串按长度添加到集合
package com.Set.TreeSet;
import java.util.Comparator;
import java.util.TreeSet;
/*
* 实现字符串类型按长度排序
* */
public class Demo04 {
public static void main(String[] args) {
TreeSet<String> strings = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int n1 = o1.length()-o2.length();//按长度比较
int n2 = o1.compareTo(o2);//长度相等就按照字母表比较规则
return n1==0 ? n2:-n1;//n2大于0,按字母表正序,否则按逆序。n2大于0 按length从小到大,否则从大到小
}
});
strings.add("helloworld");
strings.add("lisi");
strings.add("wangwu");
strings.add("beijing");
strings.add("xian");
strings.add("nanjing");
System.out.println("原素材个数:"+strings.size());
System.out.println(strings.toString());
}
}
八、Map体系集合
- Map接口的特点:
- 用于存储任意键值对(Key-Value)
- 键:无序、无下标、不允许重复(唯一)
- 值:无序、无下标、允许重复
- 方法:
- V put(K key , V value):将对象存入到集合中,关联键值。key重复则覆盖原值。
- Object get(Object key):根据键获取对应的值。
- keySet(K):返回所有的key。(重点)
- Collection values:返回包含所有值的Collection集合。
- Set<Map.Entry<K,V>>:键值匹配的Set集合。
8、1 Map接口使用
- 创建map集合
//创建map集合
Map<String,String> map = new HashMap<>();
- 添加元素
//1、添加元素
map.put("名字","张三");
map.put("年龄","18");
map.put("性别","男");
// map.put("专业","计算机");//添加重复的,value会替换掉之前的value
- 删除元素
//2、删除
map.remove("专业");
- 遍历map集合
//3、遍历
System.out.println("-------3、1keySet-------");
//Set<String> keySet= map.keySet();
for (String key: map.keySet()) {
System.out.println(key+":"+map.get(key));
}
System.out.println("-------3、2 entrySet-------");
//Set<Map.Entry<String,String>> entries = map.entrySet();
for (Map.Entry<String,String> entry: map.entrySet()) {
System.out.println(entry.getKey()+":"+ entry.getValue());
}
- 判断
//4、判断
System.out.println(map.containsKey("姓名"));
System.out.println(map.containsValue("张三"));
System.out.println(map.isEmpty());
package com.Map;
/*
* Map接口的使用
* 特点:(1)存储键值对 (2)键不能重复,值可以重复 (3)无序
* */
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Demo01 {
public static void main(String[] args) {
//创建map集合
Map<String,String> map = new HashMap<>();
//1、添加元素
map.put("名字","张三");
map.put("年龄","18");
map.put("性别","男");
map.put("专业","软件工程");
// map.put("专业","计算机");//添加重复的,value会替换掉之前的value
System.out.println("元素个数:"+map.size());
System.out.println(map.toString());
//2、删除
map.remove("专业");
System.out.println("删除之后:"+map.size());
System.out.println(map.toString());
//3、遍历
System.out.println("-------3、1keySet-------");
//Set<String> keySet= map.keySet();
for (String key: map.keySet()) {
System.out.println(key+":"+map.get(key));
}
System.out.println("-------3、2 entrySet-------");
//Set<Map.Entry<String,String>> entries = map.entrySet();
for (Map.Entry<String,String> entry: map.entrySet()) {
System.out.println(entry.getKey()+":"+ entry.getValue());
}
//4、判断
System.out.println(map.containsKey("姓名"));
System.out.println(map.containsValue("张三"));
System.out.println(map.isEmpty());
}
}
九、Map集合的实现类
- HashMap[重点]:
- JDK1.2版本,线程不安全,运行效率快;允许
null的
值和null
键。
- JDK1.2版本,线程不安全,运行效率快;允许
- HashTable:
- JDK1.0版本,线程安全,运行效率慢;不允许
null的
值和null
键。
- JDK1.0版本,线程安全,运行效率慢;不允许
- Properties:
- HashTable的子类。要求key和Value都是String。通常用于配置文件的读取。
- TreeMap:
- 实现了SortedMap接口(是Map的子接口),可以对Key自动排序。
9、1 HashMap
- HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
- HashMap 实现了 Map 接口,根据键的 HashCode 值存储数据,具有很快的访问速度,最多允许一条记录的键为 null,不支持线程同步。允许
null的
值和null
键。 - HashMap 是无序的,即不会记录插入的顺序。
- HashMap 继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口。
- 存储结构:哈希表:数组+链表+红黑树
- 使用key的hashcode和equals作为重复一依据
- 创建HashMap集合
//创建集合
HashMap<Student,String> students = new HashMap<>();
- 添加元素
//1、添加元素
Student s1 = new Student("张三",111);
Student s2 = new Student("李四",112);
Student s3 = new Student("王五",113);
students.put(s1,"北京");
students.put(s2,"天津");
students.put(s3,"上海");
students.put(new Student("张三",111),"北京");
- 删除元素
//2、删除
//students.remove(s1);
System.out.println("删除之后:"+students.size());
System.out.println(students.toString());
- 遍历HashMap集合
//3、遍历
Set<Student> studentSet = students.keySet();
System.out.println("-------3、1 keySet--------");
for (Student key: studentSet) {
System.out.println(key+":"+students.get(key));
}
System.out.println("-------3、1 keySet--------");
Set<Map.Entry<Student,String>> entries = students.entrySet();
for (Map.Entry<Student,String> entry:entries) {
System.out.println(entry.getKey()+":"+entry.getValue());
}
- 判断
//4、判断
System.out.println(students.containsKey(s1));
System.out.println(students.containsKey(new Student("张三",111)));
System.out.println(students.containsValue("北京"));
System.out.println(students.isEmpty());
9、2 HshMap源码分析
-
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // HshMap初始容量大小
-
static final int MAXIMUM_CAPACITY = 1 << 30; // HshMap的数组最大容量
-
static final float DEFAULT_LOAD_FACTOR = 0.75f; // 默认加载因子
-
static final int TREEIFY_THRESHOLD = 8; // jdk1.8 当链表长度大于8时,调整为红黑树
-
static final int UNTREEIFY_THRESHOLD = 6; // jdk1.8 当链表长度小于6时,调整为链表
-
static final int MIN_TREEIFY_CAPACITY = 64; // jdk1.8 当链表长度大于8时,并且集合元素个数大于64时,调整为红黑树
-
transient Node<K,V>[] table; //哈希表中的数组
-
size ; //元素个数
-
无参构造
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
- put方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
-
总结:
-
HashMap刚创建时,table是null,为了节省空间,当添加第一个元素是,table容量调整为16
-
当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的2倍。目的是减少调整元素的个数。
-
jdk1.8当每个链表长度大于8,并且元素个数大于等于64时,会调整为红黑树,目的提高执行效率
-
jdk1.8 当链表长度小干6时,调整成链表
-
jdk1.8以前,链表是头插入,jdk1.8以后时是尾插入
-
9、3 HashTable
- 该类实现了一个哈希表,它将键映射到值。 任何非
null
对象都可以用作键值或值。 - 方法和HashMap的方法类似。
- 子类Properties(IO流中使用)
Properties
类表示一组持久的属性。Properties
可以保存到流中或从流中加载。 属性列表中的每个键及其对应的值都是一个字符串。
9、4 TreeMap
- 实现了SortedMap接口(是Map的子接口),可以对Key自动排序。
- 存储结构:红黑树
- 方法和HashMap的方法类似
- 创建集合和自定义比较规则
//创建集合
TreeMap<Student,String> treeMap = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int n1 = o1.getStuNo()-o2.getStuNo();
int n2 = o1.getName().compareTo(o2.getName());
return n1==0 ? n2:n1;
}
});
- 添加元素
//1、添加元素
Student s1 = new Student("张三",111);
Student s2 = new Student("李四",112);
Student s3 = new Student("王五",113);
treeMap.put(s1,"北京");
treeMap.put(s2,"天津");
treeMap.put(s3,"上海");
treeMap.put(new Student("王五",113),"南京");
- 删除元素
//treeMap.remove(new Student("王五",113));
//treeMap.remove(s1);
- 遍历
//3、遍历
System.out.println("---------3、1 keySet()-------");
//Set<Student> keyset = treeMap.keySet();
for (Student st:treeMap.keySet()) {
System.out.println(st+"-----"+treeMap.get(st));
}
System.out.println("---------3、2 entrySet()-------");
for (Map.Entry<Student,String> entry: treeMap.entrySet()) {
System.out.println(entry.getKey()+"-----"+entry.getValue());
}
- 判断
//4、判断
System.out.println(treeMap.containsKey(s1));
System.out.println(treeMap.containsValue("北京"));
十、Collection工具类
- 概念:集合工具类,定义了出来存取以外的集合常用方法。
- 方法“
- public static void reverse(List<?>list) //反转集合中元素的顺序
- public static void shuffle(List<?> list) //随机重置集合元素的顺序
- public static void sort(List list) //升序排序(元素类型必须实现
Comparable接口)
package com.Set.Collections;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/*
* Collections工具类的使用
* */
public class Demo01 {
public static void main(String[] args) {
//创建集合
List<Integer> list = new ArrayList<>();
list.add(5);
list.add(20);
list.add(30);
list.add(36);
list.add(34);
list.add(44);
//sort排序
System.out.println("排序之前:"+list.toString());
Collections.sort(list);
System.out.println("排序之后:"+list.toString());
//binarySearch 二分查找
int i = Collections.binarySearch(list,36);
System.out.println("找到的位置:"+i);
//copy 复制
List<Integer> dest = new ArrayList<>();
for (int j = 0; j < list.size(); j++) {
dest.add(0);
}
Collections.copy(dest,list);
System.out.println("复制之后:"+dest.toString());
//reverse 反转
Collections.reverse(list);
System.out.println("反转之后:"+list.toString());
//shuffle 打乱排序
Collections.shuffle(list);
System.out.println("打乱排序之后:"+list.toString());
//补充List 转为数组
System.out.println("-----List 转为数组----");
Integer[] arr = list.toArray(new Integer[0]);
System.out.println("数组长度:"+arr.length);
System.out.println("转换的数组:"+Arrays.toString(arr));
//数组转为集合
System.out.println("-----List 转为数组----");
String[] strings ={"张三","李四","王五","王麻子"};
System.out.println("----原数组为:------");
for (String string : strings) {
System.out.print(string + "\t");
}
System.out.println();
List<String> list1 = Arrays.asList(strings);
//数组转换为集合后,不能添加和删除,因为数组的长度是固定的,转换为集合后也是固定的
System.out.println("数组转换为集合:"+list1.size());
System.out.println("数组转换为集合:"+list1.toString());
//把基本类型的数组转化为集合需要先转换为包装类
Integer[] nums = {1,4,5,23,6};
List<Integer> list2 = Arrays.asList(nums);
System.out.println(list2);
}
}
十一、总结
- 集合的概念:
- 对象的容器,和数组类似,定义了对多个对象进行操作的常用方法。
- List集合:
- 有序、有下标、元素可以重复。(ArrayList、LinkedList、Vector)
- Set集合:
- 无序、无下标、元素不可重复。(HashSet、TreeSet)
- Map集合:
- 存储一对数据,无序、无下标,键不可重复,值可重复。(HashMap、HashTable、TreeMap)
- Collections:
- 集合工具类,定义了除了存取以外的集合常用方法。