【1】数组,集合都是对多个数据进行存储操作的,简称为容器。
PS:这里的存储指的是内存层面的存储,而不是持久化存储(.txt,.avi,.jpg,数据库)。
特点:
(1)数组一旦指定了长度,那么长度就被确定了,不可以更改。
int[] arr = new int[6];
(2)数组一旦声明了类型以后,数组中只能存放这个类型的数据。数组中只能存放同一种类型的数据。
int[] arr,String[] s,double[] d.....
缺点:
(1)数组一旦指定了长度,那么长度就被确定了,不可以更改。
(2)删除,增加元素 效率低。
(3)数组中实际元素的数量是没有办法获取的,没有提供对应的方法或者属性来获取
(4)数组存储:有序,可重复 ,对于无序的,不可重复的数组不能满足要求。
【2】正因为上面的缺点,引入了一个新的存储数据的结构---》集合
增加:add(E e) addAll(Collection extends E> c)
删除:clear() remove(Object o)
修改:
查看:iterator() size()
判断:contains(Object o) equals(Object o) isEmpty()
public static void main(String[] args) {
Collection col = new ArrayList();
//添加
//1、add(E e)
col.add(18);
System.out.println(col);
//2、addAll(Collection extends E> c)
List list = Arrays.asList(new Integer[]{11, 15, 3, 7, 1});
col.addAll(list);
//删除、
//1、clear()
//col.clear();清空集合
System.out.println(col);
System.out.println("集合中元素的数量为:"+col.size());
System.out.println("集合是否为空:"+col.isEmpty());
//2、remove(Object o)
boolean isRemove = col.remove(15);
System.out.println(col);
System.out.println("集合中数据是否被删除:"+isRemove);
//判断
Collection col2 = new ArrayList();
col2.add(18);
col2.add(12);
col2.add(11);
col2.add(17);
Collection col3 = new ArrayList();
col3.add(18);
col3.add(12);
col3.add(11);
col3.add(17);
System.out.println(col2.equals(col3));
System.out.println(col2==col3);//地址一定不相等 false
System.out.println("是否包含元素:"+col3.contains(117));
}
2、注意:
接口不能创建对象,利用实现类创建对象
Collection col = new ArrayList();
只能存放引用数据类型的数据,不能是基本数据类型
基本数据类型自动装箱,对应包装类。int--->Integer
Collection集合遍历方式:方式一:
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(18);
col.add(12);
col.add(11);
col.add(17);
col.add("abc");
col.add(9.8);
//方式1:增强for循环
for(Object o:col){
System.out.println(o);
}
}
方式二:
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(18);
col.add(12);
col.add(11);
col.add(17);
col.add("abc");
col.add(9.8);
//方式2:iterator()迭代器
Iterator it = col.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
注意:不能用普通for循环遍历,因为没有可以调用的方法
(一)List系列集合
1、常用方法
增加:add(int index, E element)
删除:remove(int index) remove(Object o)
修改:set(int index, E element)
查看:get(int index)
添加:
List list = new ArrayList();
list.add(13);
list.add(17);
list.add(6);
list.add(-1);
list.add(2);
list.add("abc");
System.out.println(list);
//在某个索引处添加
list.add(3,66);
System.out.println(list);
list.set(3,77);
System.out.println(list);
删除:
list.remove(2);
//在集合中存入的是Integer类型数据的时候,调用remove方法调用的是:remove(int index)
//根据索引来删除
System.out.println(list);
list.remove("abc");
System.out.println(list);
修改:
list.set(4,55); System.out.println(list); list.set(4,99); System.out.println(list);
查看:
Object o = list.get(0); System.out.println(o);2、List遍历集合方式
方式一:
//方式1:普通for循环:
System.out.println("---------------------");
for(int i = 0;i
方式二:
//方式2:增强for循环:
System.out.println("---------------------");
for(Object obj:list){
System.out.println(obj);
}
方式三:
//方式3:迭代器:
System.out.println("---------------------");
Iterator it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
ArrayList:
包含一定泛型类容,以及对集合的一些补充
CSDNhttps://mp.csdn.net/mp_blog/creation/editor/124676600
泛型:
方便以后API的阅读
1、概述:
泛型就相当于标签
形式:<>
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,
JDK1.5之 后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。
2、自定义泛型
泛型类的定义和实例化:
//GenericTes就是一个普通的类
//GenericTest 就是一个泛型类
//<>里面就是一个参数类型,这个类型现在是不确定的,相当于一个占位
public class GenericTest {
int age;
String name;
E sex;
public void a(E n){
}
public void b(E[] m){
}
}
class Test{
//这是main方法,程序的入口
public static void main(String[] args) {
//GenericTest进行实例化:
//(1)实例化的时候不指定泛型:如果实例化的时候不明确的指定类的泛型,那么认为此泛型为Object类型
GenericTest gt1 = new GenericTest();
gt1.a("abc");
gt1.a(17);
gt1.a(9.8);
gt1.b(new String[]{"a","b","c"});
//(2)实例化的时候指定泛型:---》推荐方式
GenericTest gt2 = new GenericTest<>();
gt2.sex = "男";
gt2.a("abc");
gt2.b(new String[]{"a","b","c"});
}
}
继承情况:
(1)父类指定泛型
class SubGenericTest extends GenericTest{
}
class Demo{
//这是main方法,程序的入口
public static void main(String[] args) {
//指定父类泛型,那么子类就不需要再指定泛型了,可以直接使用
SubGenericTest sgt = new SubGenericTest();
sgt.a(19);
}
}
(2)父类不指定泛型
如果父类不指定泛型,那么子类也会变成一个泛型类,那这个E的类型可以在创建子类对象的时候确定
class SubGenericTest2 extends GenericTest{
}
class Demo2{
//这是main方法,程序的入口
public static void main(String[] args) {
SubGenericTest2 s = new SubGenericTest2<>();
s.a("abc");
s.sex = "女";
}
}
细节:
(1)泛型类可以使用多个参数类型:
public class TestGeneric{
A age;
B name;
C sex;
public void a(A m,B n,C x){
}
}
(2) 泛型类的构造器的写法:
public TestGeneric(){
}
(3)不同泛型的引用类型不可以相互赋值
public void(){
ArrayList list1 = null;
ArrayList list2 = null;
//list1 = list2;报错
}
(4)当泛型参数存在继承情况时
Object obj = new Object();
String s = new String();
obj = s;//多态的一种形式
Object[] objArray = new Object[10];
String[] strArray = new Stirng[10];
objArray = strArray;//多态的一种形式
List
总结:当A和B是子类父类的关系的时候,G和G不存在继承的关系,它们是并列的关系
(5)泛型如果不指定,对应类型就会变为Object类型,即可以添加任何引用类型的数据
(6)泛型类中的静态方法不能调用类的泛型:
public class TestGeneric{
A age;
B name;
C sex;
public static int c(A a){
//报错
return 10;
}
}
原因:static方法在对象之前创建,而泛型类型还未确定
(7)不可以直接使用E[]的创建
public void a(A a,B b,C c){
//不可以:A[] i = new A[10];
A[] i = (A[])new Object[]10;
}
泛型方法:
泛型方法对应的那个泛型参数类型,和当前所在的这个类,是否是泛型类,泛型是什么无关
public class TestGeneric {
//不是泛型方法 (不能是静态方法)
public static void a(E e){
}
//是泛型方法
public static void b(T t){
}
}
class Demo{
//这是main方法,程序的入口
public static void main(String[] args) {
TestGeneric tg = new TestGeneric<>();
tg.a("abc");
tg.b("abc");
tg.b(19);
tg.b(true);
}
注:
①泛型方法定义的时候,前面要加上,如果不加的话,会把T当做一种数据类型,然而代码中没有T类型那么就会报错
②泛型方法可以是静态方法
③ 泛型方法的泛型在调用方法时传入
通配符:
(1)概述:A 和 B是子类父类的关系,G和G不存在子类父类关系,是并列的
加入通配符?后,G>就变成了 G和G的父类
List
(2)通配符使用细节:
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);
}
泛型受限:
定义一个Student类和Person类,Student类继承Person类
定义三个并列关系的集合
List
(1)使用泛型受限:泛型的上限
List extends Person> list1 = null;
//list1 = a;报错
list1 = b;
list1 = c;
解释:List extends Person>就相当于List extends Person>是List的父类,是List的父类
(2)使用泛型受限:泛型的下限
List super Person> list2 = null;
list2 = a;
list2 = b;
//list3 = c;报错
解释: List super Person>就相当于List super Person>是List的父类,是List的父类
LinkedList:
1、常用方法
增加 addFirst(E e) addLast(E e)
offer(E e) offerFirst(E e) offerLast(E e)
删除 poll()
pollFirst() pollLast() ---》JDK1.6以后新出的方法,提高了代码的健壮性
removeFirst() removeLast()
查看 element()
getFirst() getLast()
indexOf(Object o) lastIndexOf(Object o)
peek()
peekFirst() peekLast()
LinkedList list = new LinkedList<>();
list.add("aaaaa");
list.add("bbbbb");
list.add("ccccc");
list.add("ddddd");
list.add("eeeee");
list.add("bbbbb");
list.add("fffff");
list.addFirst("jj");//添加元素在头
list.addLast("hh");//添加元素在尾端
list.offer("kk");//添加元素在尾端
list.offerFirst("pp");//添加元素在头
list.offerLast("rr");//添加元素在尾端
System.out.println(list);//LinkedList可以添加重复数据
System.out.println(list.poll());//删除头上的元素并且将元素输出
System.out.println(list.pollFirst());
System.out.println(list.pollLast());
System.out.println(list.removeFirst());
System.out.println(list.removeLast());
System.out.println(list);//LinkedList可以添加重复数据
2、LinkedList遍历集合方式
(1)普通for循环
for(int i = 0;i
(2)增强for循环
for(String s:list){
System.out.println(s);
}
(3)迭代器
//下面这种方式好,节省内存
for(Iterator it = list.iterator();it.hasNext();){
System.out.println(it.next());
}
3、LinkedList简要底层原理图
(二)Set系列集合
Set集合的功能基本上与Collection的一致
特点:
①无序:存取顺序不一致
②不重复:可以去除重复
③无索引:没有带索引的方法,所以不能使用for循环遍历,也不能通过索引来获取元素
HashSet:
特点:
无序、不重复、无索引
Set sets = new HashSet<>();
sets.add("Java");
sets.add("Java");
sets.add("MySQL");
sets.add("MySQL");
sets.add("HTML");
sets.add("HTML");
sets.add("SpringBoot");
sets.add("SpringBoot");
System.out.println(sets);
[MySQL, Java, HTML, SpringBoot]
//无序
底层原理:
①JDLK8之前的,底层使用数组+链表组成
●创建一个默认长度16的数组,数组名table
●根据元素的哈希值跟数组长度求余计算出应存入的位置(哈希算法)
●判断当前位置是否为null,如果是null直接存入
●如果位置不为空,则调用equals方法比较
●如果一样,则不存,如果不一样,则存入数组
◑新元素占老元素位置,指向老元素
②JDK8开始后,底层采用数组+链表+红黑树组成
◑新元素挂在老元素下面
当挂在元素下面的数据过多时,查询性能降低,于是,当链表长度超过8的时候,自动转换成红黑树
哈希值:是JDK根据对象的地址,按照某种规则算出来的int类型的数值
Object类的API:public int hashCode():返回对象的哈希值
对象哈希值的特点:
①同一个对象多次调用hashCode()方法返回的哈希值是相同的
②默认情况下,不同对象的哈希值是不同的
String name = "aaaa";
System.out.println(name.hashCode());
System.out.println(name.hashCode());
String name1 = "bbbb";
System.out.println(name1.hashCode());
System.out.println(name1.hashCode());
2986048
2986048
3016832
3016832
注意:
如果往HashSet中放入数据,需要重写hashCode和equals
学生类:
public class Student {
private String name;
private int age;
private char sex;
public Student() {
}
public Student(String name, int age, char sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
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 char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + ''' +
", age=" + age +
", sex=" + sex +
'}';
}
}
不重写的话:
public static void main(String[] args) {
Set sets = new HashSet<>();
Student s1 = new Student("无恙",20,'男');
Student s2 = new Student("无恙",20,'男');
Student s3 = new Student("周维",21,'男');
sets.add(s1);
sets.add(s2);
sets.add(s3);
System.out.println(sets);
}
[Student{name='周维', age=21, sex=男}, Student{name='无恙', age=20, sex=男}, Student{name='无恙', age=20, sex=男}]
//添加了两个信息一样的学生
重写:在学生类里右键,生成
一路next,即可生成
@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 && sex == student.sex && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, sex);
}
重写后:
[Student{name='无恙', age=20, sex=男}, Student{name='周维', age=21, sex=男}]
结论:
哈希表是一种对于增删改查数据性能都较好的结构
LinkedHashSet:
特点:有序、不重复、无索引
Set sets = new LinkedHashSet<>();
sets.add("Java");
sets.add("Java");
sets.add("MySQL");
sets.add("MySQL");
sets.add("HTML");
sets.add("HTML");
sets.add("SpringBoot");
sets.add("SpringBoot");
System.out.println(sets);
[Java, MySQL, HTML, SpringBoot]
//有序
原理:底层数据结构依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序
TreeSet:
特点:
①可排序、不重复、无索引(按元素大小默认升序)
②TreeSet集合是基于红黑树的数据结构排序的,增删改查性能都较好
③TreeSet集合是一定要排序的,可将元素按照指定的规则进行排序
默认排序规则:
●对于数值型,Integer,Double,官方默认按照大小进行升序排序
●对于字符串类型,默认按照首字符的编号升序排序
●对于自定义的Student对象,TreeSet无法直接排序(需制定排序规则)
数值型:
public static void main(String[] args) {
Set sets = new TreeSet<>();
sets.add(23);
sets.add(12);
sets.add(13);
sets.add(32);
sets.add(21);
sets.add(14);
System.out.println(sets);
}
[12, 13, 14, 21, 23, 32]
字符型:
Set sets1 = new TreeSet<>();
sets1.add("Java");
sets1.add("Java");
sets1.add("About");
sets1.add("Python");
sets1.add("UI");
sets1.add("UI");
System.out.println(sets1);
[About, Java, Python, UI]
自定义:
定义一个苹果类:
public class Apple {
private String name;
private String color;
private double price;
private int weight;
public Apple() {
}
public Apple(String name, String color, double price, int weight) {
this.name = name;
this.color = color;
this.price = price;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Apple{" +
"name='" + name + ''' +
", color='" + color + ''' +
", price=" + price +
", weight=" + weight +
'}';
}
}
Set apples = new TreeSet<>();
apples.add(new Apple("红富士","红色",9.9,500));
apples.add(new Apple("青苹果","绿色",15.9,300));
apples.add(new Apple("绿苹果","青色",29.9,400));
apples.add(new Apple("黄苹果","黄色",9.8,500));
System.out.println(apples);
因为无法对Apple排序,所以
所以,自定义排序规则
方法一:让自定义的类实现Comparable接口重写的comparaTo方法来制定比较规则
public class Apple implements Comparable
@Override
public int compareTo(Apple o) {
//按照重量进行比较
//去重量重复的元素
return this.weight - o.weight >= 0 ? 1 :-1;//保留重量重复的元素
}
结果:
[Apple{name='青苹果', color='绿色', price=15.9, weight=300}, Apple{name='绿苹果', color='青色', price=29.9, weight=400}, Apple{name='红富士', color='红色', price=9.9, weight=500}, Apple{name='黄苹果', color='黄色', price=9.8, weight=500}]
方式二:TreeSet集合有参数构造器,可以设置Comparator接口对应的比较对象,来制定比较规则
Set apples = new TreeSet<>(new Comparator() {
//方式二:集合自带比较器对像进行规则制定
@Override
public int compare(Apple o1, Apple o2) {
//return o1.getWeight() - o2.getWeight(); 升序
return o2.getWeight() - o1.getWeight(); //降序
}
});
注意:如果TreeSet集合存储的对象有实现比较规则,集合也自带比较规器,默认使用集合自带的比较器排序
总结:
1、如果希望元素可以重复,又有索引,索引查询要快
●用ArrayList集合(基于数组,用的最多)
2、如果希望元素可以重复,又有索引,增删首尾操作快
●用LinkedList集合(基于链表)
3、如果希望增删改查都快,但元素不重复、无序、无索引
●用HashSet集合(基于哈希表)
4、如果希望增删改查都很快,但元素不重复、有序、无索引
●用Linked Hash Set集合(基于哈希表和双链表)
5、如果要对对象进行排序
●用TreeSet集合(基于红黑树,后续也可用List集合实现排序)
二、Map
1、Map部分整体结构图
2、概述
●Map集合是一种双列集合,每个元素包含两个数据
●Map集合的每个元素的格式:key=value(键值对元素)
●Map集合也被称为键值对集合
对比:
Collection集合的格式:
[元素1,元素2,元素3...]
Map集合的完整格式:
{key1=value1,key2=value2,key3=value3}
●key称为键,value称为值
3、使用场景
{商品1=商品件数,商品2=3,商品3=3,商品4=2}
4、 Map集合体系特点
●Map集合的特点都是由键决定的
●Map集合的键是无序、不重复、无索引的,值可以重复
●Map集合后面重复的键对应的值会覆盖前面重复键的值
●Map集合的键值对都可以为null
5、常用API
方法名称 说明 V put(K key,V value) 添加元素 V get(Object key) 根据建获取对应值 V remove(Object key) 根据键删除键值对元素 void clear() 移除所有键值对元素 boolean containsKey(Object key) 判断集合是否包含指定的键 boolean containsValue(Object value) 判断集合是否包含指定的值 boolean isEmpty() 判断集合是否为空 Set keySet() 获取全部键的集合 Collection values() 获取全部值的集合 putAll() 合并其他Map集合 int size() 获取集合的长度,也就是集合中键值对的个数
public static void main(String[] args) {
Map maps = new HashMap<>();
maps.put("ipthoex",20);
maps.put("小米",30);
maps.put("华为",40);
//获取所有的键
Set keys= maps.keySet();
System.out.println(keys);
//获取所有的值
Collection values = maps.values();
System.out.println(values);
System.out.println("--------------------------");
//合并集合
Map map1 = new HashMap<>();
map1.put("鸡腿",2);
map1.put("鸡翅",2);
Map map2 = new HashMap<>();
map2.put("鸭腿",2);
map2.put("鸭翅",2);
map1.putAll(map2);
System.out.println(map1);
}
6、遍历
方式一:键找值
先获取Map集合的全部键的Set集合,遍历间的Set集合,然后通过键提取对应值
涉及到的API:keySet() || get()
public static void main(String[] args) {
Map maps = new HashMap<>();
maps.put("ipthoex",20);
maps.put("小米",30);
maps.put("华为",40);
//1、拿到集合的全部键
Set keys= maps.keySet();
//2、遍历每个键,根据键提取值
for (String key:keys){
int value = maps.get(key);
System.out.println(key + "===>" + value);
}
方式二:键值对流程
先把Map集合转换为Set集合,Set集合都是键值对实体类型,遍历Set集合,然后提取键以及提取值
涉及到的API:Set>entrySet() || getKey() || getValue()
//1、把Map集合转换为Set集合
Set> entries = maps.entrySet();
//2、开始遍历
for (Map.Entry entry : entries){
String key = entry.getKey();
int value = entry.getValue();
System.out.println(key + "===>" + value);
}
方式三:lambda表达式
(一)HashMap5
使用的最多
特点:
元素是按照键是无序、不重复、值不做要求
Map maps = new HashMap<>();//一行经代码
maps.put("鸿星尔克",3);
maps.put("枸杞",100);
maps.put("Java",1);
maps.put("Java",100);//覆盖前面的数据
maps.put(null,null);
System.out.println(maps);
{null=null, Java=100, 枸杞=100, 鸿星尔克=3}
LinkedHashMap:
特点:元素是按照键是有序、不重复、值不做要求
(二)HashTable
(三)TreeMap
1、特点
元素是按照键是排序、不重复、值不做要求
后续继续补充



