Java学习(4):集合

本文详细介绍了Java集合框架中的Collection接口及其子接口List和Set的特点、常用API、遍历方式,以及泛型的概念和使用。同时,文章涵盖了数组、链表、二叉树等基础数据结构,以及ArrayList、HashSet、TreeSet等具体集合类的使用示例。此外,还讨论了可变参数和Map集合,包括HashMap、TreeMap的特性及操作方法。
摘要由CSDN通过智能技术生成


一、集合

1.数组 类型确定,长度确定。
2.集合的大小不固定,启动后可以动态变化,类型也可以不固定。

1. Collection集合的体系特点

集合 Collection 单列 、 Map 双列(键值对)
在这里插入图片描述

Collection接口是所有集合的根接口。
List系列集合:添加元素是有序、可重复、有索引。
- ArrayList、LinekdList:有序、可重复、有索引。
Set系列结合:
- HashSet:无序、不重复、无索引。
- LinkedHashSet:有序、不重复、无索引。
- TreeSet: 按照大小默认升序排序、不重复、无索引。

//有序 可重复 有索引
ArrayList list = new ArrayList();
list.add("a");
list.add("a");
list.add("e");
list.add("f");

System.out.println(list); //[a, a, e, f]

System.out.println(list.get(1)); //a

//无序 不重复 无索引
HashSet hs = new HashSet();
hs.add("a");
hs.add("a");
hs.add("e");
hs.add("f");
System.out.println(hs); //[a, e, f]

2.Collection 集合的常用API

集合Collection接口中的方法(所有实现类必须拥有的方法)。

//接口多态的方式调用
Collection<String> co = new ArrayList<String>();
//添加元素
co.add("abc");
co.add("bcd");
System.out.println(co);//[abc, bcd]
//清空集合中的元素
co.clear();
System.out.println(co);//[]
if(co.isEmpty()){  //是否为空
    System.out.println("empty");
}
//添加元素
co.add("abc");
co.add("bcd");
if(co.contains("bcd")){ //判断是否存在
    co.remove("bcd");  //删除指定数据
}
System.out.println(co);//[abc]
System.out.println(co.size()); //1 集合长度
Object[] objects = co.toArray();//转成数组


Collection<String> c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("赵敏");
c2.add("殷素素");
// addAll把c2集合的元素全部倒入到c1中去。
c1.addAll(c2);

泛型用来约束指定类型的数据,必须使用引用类型包装类。

3.Collection 集合的遍历方式

  1. Iterator接口 迭代器
    Java中提供了许多集合,他们在存储时方式不同。取出时通过一种通用的方式。如果有就取出。
    接口Iterator :两个抽象方法
    boolean hasNext() 判断集合中还有没有可以被取出的元素,如果有返回true。
    next() 取出集合中的下一个元素
    使用ArrayList集合的对象
    Iterator it = array.iterator(); 运行结果就是Iterator接口的实现类对象
    It是接口的实现类对象,调用方法hasNext和next集合元素迭代
Collection<String> co = new ArrayList<String>();
co.add("abc");
co.add("bcd");
co.add("abc");
Iterator<String> it = co.iterator();
while(it.hasNext()){
   System.out.println(it.next());
}

//for写法
for(Iterator<String> it = co.iterator();it.hasNext();){
   System.out.println(it.next());
}


//使用迭代器遍历集合时改变集合会报错。
Iterator<String> it = co.iterator();
while(it.hasNext()){
   String val = it.next();
   if(val=="abc")
      Co.add("aa"); //迭代器工作时 不可以改变集合
   System.out.println(val);
}
  1. 增强for循环

好处:代码少,方便遍历
弊端:没有索引,不能操作容器里面的元素

for(数据类型 变量名 : 数组或集合){
    System.out.println(变量名);
}


ArrayList<String> djdj = new ArrayList<>();
djdj.add("a");

for(String s : djdj){
	//djdj.remove("java"); //报错 不可以这样做
    System.out.println(s);
}



djdj.for回车 idea自动补全

  1. lambda表达式遍历集合
ArrayList<String> djdj = new ArrayList<>();
djdj.add("a");

djdj.forEach(s->{
	//djdj.remove("a"); //报错
    System.out.println(s);
});



for(int i = 0;i<djdj.size();i++){
	djdj.remove("a"); //这种可以删 但是会漏删数据
}

for(int i = djdj.size() - 1;i>=0;i--){
	djdj.remove("a") //从后面往前删
}


4.Collection集合存储自定义类型的对象

Student student = new Student();
student.name = "aa";
student.age=21;
ArrayList<Student> students = new ArrayList<>();

students.forEach(s->{
    System.out.println(s.name);
});

二、 数据结构

数据结构是计算机底层存储、组织数据的方式,是指数据相互之间是以什么方式排列在一起的。

1.栈

先进后出,类似子弹夹,最先进入的最后出 最后进入的最先出。
在这里插入图片描述

2.队列

先进先出,类似排队做核酸,先做完的先走,后做完的后走。
在这里插入图片描述

3.数组

内存中的一块连续区域,查询速度快,通过索引快速找到指定值,但是删除效率低,所有元素向迁移。添加效率低,在指定位置添加后 所有元素后移。
在这里插入图片描述

4.链表

链表中的元素在内存中是不连续的,每个元素节点包含数据值和下一个元素的地址。
链表查询慢,无论查询哪个数据都要从头开始找。
但是增删相对快 在ac之间增加一个数据b 1,将数据a对应的下一个数据指向b,2.将数据b的下一个地址指向数据c。
在这里插入图片描述

5.二叉树

只能有一个根节点,每个节点最多支持2个直接子节点。
在这里插入图片描述

6.二叉查找树

提高检索数据的性能
在这里插入图片描述
每一个节点上最多两个节点,
左子树上所有的节点值都小于根节点
右子树上所有的节点值都大于根节点
在查找数据的时候 如果大于根节点就直接去右子树上查找,所以查找效率高。

7.平衡二叉树

在这里插入图片描述
二叉查找树存放时,如果数据都是大于根节点的,那么数据一直往右子树上插,所以会有瘸子现象,查询速度就会变慢。因此出现了平衡二叉树。将根节点变为11,左旋一下。

任意节点的左右两个子树的高度差不超过1,添加数据的时候会自动左旋右旋,保证二叉树的平衡。

8.红黑树

每一个节点可以是红或者黑,红黑树不是通过高度平衡的,而是通过颜色。
根节点必须是黑色的,子节点是黑红交替的。
如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的。
在这里插入图片描述

在这里插入图片描述

三、List集合

有序的collection,可以对每个元素的插入位置进行精确的控制,根据整数索引控制。

  • 实现类:ArrayList、LinekdList:有序,可重复,有索引。
  • ArrayList底层是基于数组实现的,查询元素快,增删相对慢。
  • LinkedList底层基于双链表实现的,查询元素慢,增删首尾元素是非常快的。

对一个集合的操作如果大多是首尾操作那么推荐使用LinkedList类。

LinkedList<Integer> link = new LinkedList<Integer>();
link.add(1);
link.addLast(2);//添加到当前末尾
link.add(3);
link.addFirst(4); //添加到当前开头
if(!link.isEmpty()){ //link.size() != 0
    System.out.println(link); //[4, 1, 2, 3]
    System.out.println(link.getFirst()); //获取开头
    System.out.println(link.getLast()); //获取结尾
}
link.removeFirst();
link.removeLast();
System.out.println(link);//[1, 2]

四、 泛型

使用集合时,明确集合中的元素类型。不会像arrayList那样object强转异常。
Java泛型是伪泛型
ArrayList 编译手段
arr.add(“”) 不是String的时候编译失败
但是编译后的class文件,是没有泛型的

修饰符 class 类名<代表泛型的变量> { }

//例如,API中的ArrayList集合:
class ArrayList<E>{
    public boolean add(E e){ }
  	public E get(int index){  }
}
 
//创建对象时,确定泛型的类型
ArrayList<String> list = new ArrayList<String>();
//此时,变量E的值就是String类型
class ArrayList<String>{
  public boolean add(String e){ }
  public String get(int index){  }
}
ArrayList<Integer> list = new ArrayList<Integer>();
//此时,变量E的值就是Integer类型
class ArrayList<Integer>{
     public boolean add(Integer e){ }
     public Integer get(int index){  }
}

?泛型的通配符

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
 
public class Main {
    public static void main(String[] args){
        ArrayList<Integer> nums = new ArrayList<Integer>();
        nums.add(1);
        nums.add(2);
 
        ArrayList<String> strs = new ArrayList<String>();
        strs.add("abc");
        strs.add("def");
 
        iterator(nums);
        iterator(strs);
    }
    public static void iterator(Collection<?> coll){
        Iterator<?> it = coll.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
}

泛型的限定

//创建3个集合对象
ArrayList<ChuShi> cs = new ArrayList<ChuShi>();
ArrayList<FuWuYuan> fwy = new ArrayList<FuWuYuan>();
ArrayList<JingLi> jl = new ArrayList<JingLi>();
//每个集合存储自己的元素
cs.add(new ChuShi("张三", "后厨001"));
cs.add(new ChuShi("李四", "后厨002"));
fwy.add(new FuWuYuan("翠花", "服务部001"));
fwy.add(new FuWuYuan("酸菜", "服务部002"));
jl.add(new JingLi("小名", "董事会001", 123456789.32));
jl.add(new JingLi("小强", "董事会002", 123456789.33));
iterator(jl);
iterator(fwy);
iterator(cs);
 
// ? extends Car 上限 必须是Car子类
// ? super Car 下限 必须是Car父类
//方法参数: 控制,可以传递Employee对象,也可以传递Employee的子类的对象
public static void iterator(ArrayList<? extends Employee> array){
   Iterator<? extends Employee> it = array.iterator();
   while(it.hasNext()){
     //获取出的next() 数据类型,是什么Employee
     Employee e = it.next();
     e.work();
   }
}
 

//泛型类
public class PageOf<T> {
    private Integer page;
    private Integer rowsCount;
    private Integer totalCount;
    private Integer totalPages;
    private List<T> data;
}

//使用
PageOf<MyGoodsDto> pageData = new PageOf<>();
pageData.setData(list);
pageData.setTotalPages(offsetPage.getPages());
pageData.setTotalCount((int) offsetPage.getTotal());
pageData.setRowsCount(offsetPage.getPageSize());
pageData.setPage(param.getPage());
return pageData;
//泛型接口

public interface Mapper<T>{
}

public interface AccountDao extends Mapper<Account> {

}

//泛型接口可以约束实现类,实现类可以在实现接口的时候传入自己操作的数据类型.
//就可以使用该接口里面的方法,重写或者直接调用。

五、Set集合

无序:存取顺序不一致,一次。
不重复:可以去除重复数据。
无索引:没有带索引的方法,不能使用for循环遍历。

实现类:

  1. HashSet:无序、不重复、无索引。
  2. LinkedHashSet:有序,不重复,无索引。
  3. TreeSet:排序、不重复、无索引。
HashSet<String> set = new HashSet<String>();
set.add("haha1");
set.add("haha1");//如果存入重复的,只存进去一个。
set.add("def3");
set.add("ghi4");

for(String s : set){
   System.out.println(s); //def3 haha1 ghi4 执行几次都是这个顺序
}

HashSet集合底层采取哈希表存储的数据。
哈希表是一种对增删改查数据性能较好的结构。

哈希表的组成 1.8之前 数组+链表组成
1.8之后 底层采用 数组+链表+红黑树 组成

哈希值
是JDK根据对象的地址,按照某种规则算出来的int类型的数值。
Object API : public int hashCode() 返回对象的哈希值。
同一个对象调用hashCode()方法多次返回的哈希值是相同的。

LinkedHashSet
这里的有序指的是保证存储和读取的元素顺序一致。
底层原理,依然是哈希表,但是每个元素又额外多了一个双链表的机制记录存储的顺序。

Set<String> sets = new LinkedHashSet<>(); // 有序  不重复 无索引
sets.add("MySQL");
sets.add("MySQL");
sets.add("Java");
sets.add("Java");
sets.add("HTML");
sets.add("HTML");
sets.add("SpringBoot");
sets.add("SpringBoot");
System.out.println(sets);

TreeSet
可排序:按照元素的大小默认升序排序。
底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
如果存储的是对象则需要给一个排序规则。

Set<Apple> apples = new TreeSet<>(( o1,  o2) ->  Double.compare(o2.getPrice() , o1.getPrice())  );
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);

Collections.addAll 像集合添加一批数据。
Collections.shuffle 打乱顺序
Collections.sort 排序

List<String> names = new ArrayList<>();
//names.add("楚留香");
//names.add("胡铁花");
//names.add("张无忌");
//names.add("陆小凤");
Collections.addAll(names, "楚留香","胡铁花", "张无忌","陆小凤");
System.out.println(names);

// 2、public static void shuffle(List<?> list) :打乱集合顺序。
Collections.shuffle(names);
System.out.println(names);

// 3、 public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。 (排值特性的元素)
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 12, 23, 2, 4);
System.out.println(list);
Collections.sort(list);
System.out.println(list);

六、可变参数

前提:方法参数的数据类型需要确定,参数的个数不确定。

public static void main(String[] args) {
        sum(); // 1、不传参数
        sum(10); // 2、可以传输一个参数
        sum(10, 20, 30); // 3、可以传输多个参数
        sum(new int[]{10, 20, 30, 40, 50}); // 4、可以传输一个数组
    }

    /**
       注意:一个形参列表中只能有一个可变参数,可变参数必须放在形参列表的最后面
     * @param nums
     */
    public static void sum(  int...nums){
        // 注意:可变参数在方法内部其实就是一个数组。 nums
        System.out.println("元素个数:" + nums.length);
        System.out.println("元素内容:" + Arrays.toString(nums));
    }

一个形参列表只能有一个可变参数。
可变参数必须放在形参列表的最后面。

七、Map集合

键值对集合,不能重复key,常用集合为HashMap集合、LinkedHashMap集合。

Map集合是一种双列集合,每个元素都有两个数据
每个元素的格式 key=value。也被称为键值对集合。

在这里插入图片描述
HashMap:元素无序,不重复,无索引。
LinkedHashMap:有序,不重复,无索引。
TreeMap:排序,不重复,无索引。

LinkedHashMap

Map<String, Integer> maps = new LinkedHashMap<>();
maps.put("鸿星尔克", 3);
maps.put("Java", 1);
maps.put("枸杞", 100);
maps.put("Java", 100); // 覆盖前面的数据
maps.put(null, null);
System.out.println(maps); //有序

HashMap

HashMap<Integer,String> map = new HashMap<Integer,String>();
//添加键值对
map.put(1,"a");
map.put(2,"b");
map.put(1,"c"); //key重复 会覆盖原有value
System.out.println(map);//{1=c, 2=b}
 
String val = map.get(1); //get通过key获取value
System.out.println(val); //c
 
map.remove(1); //移除

map.containKey(1); //是否存在key
map.containValue("c"); //是否存在值 


// 判断集合是否为空,为空返回true ,反之!
System.out.println(maps.isEmpty());

//返回set keySet()返回所有key
Set<Integer> keys = map.keySet();
 
for(int i : keys){
    System.out.println(i+":" +map.get(i));
}

Entry 键值对对象
Entry将键值对的对应关系封装成了对象,在遍历map集合时,就可以从每一个键值对Entry对象中获取对应的key与value。

//返回set entrySet()返回所有entry对象
Set<Map.Entry<Integer, String>> entrys = map.entrySet();
 
for (Map.Entry<Integer, String> entry : entrys) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}
 

HashTable 线程安全集合,运行速度比较慢 不允许存null key,null value
HashMap 线程不安全集合,运行速度快 可以存null key,null value

存储对象

 // Map集合是根据键去除重复元素
Map<Student, String> maps = new HashMap<>();

Student s1 = new Student("无恙", 20, '男');
Student s2 = new Student("无恙", 20, '男');
Student s3 = new Student("周雄", 21, '男');

maps.put(s1, "北京");
maps.put(s2, "上海");
maps.put(s3, "广州");

System.out.println(maps);

TreeMap 存对象时给个排序规则

Map<Integer, String> maps1 = new TreeMap<>();
maps1.put(13 , "王麻子");
maps1.put(1 , "张三");
maps1.put(3 , "县长");
System.out.println(maps1);

// TreeMap集合自带排序。  可排序 不重复(只要大小规则一样就认为重复)  无索引
Map<Apple, String> maps2 = new TreeMap<>((Apple o1,Apple o2)-> {
    return Double.compare(o2.getPrice() , o1.getPrice()); // 按照价格降序排序!
});
maps2.put(new Apple("红富士", "红色", 9.9, 500), "山东" );
maps2.put(new Apple("青苹果", "绿色", 15.9, 300), "广州");
maps2.put(new Apple("绿苹果", "青色", 29.9, 400), "江西");
maps2.put(new Apple("黄苹果", "黄色", 9.8, 500), "湖北");

八、集合嵌套

// 1、要求程序记录每个学生选择的情况。
// 使用一个Map集合存储。
Map<String, List<String>> data = new HashMap<>();

// 2、把学生选择的数据存入进去。
List<String> selects = new ArrayList<>();
Collections.addAll(selects, "A", "C");
data.put("罗勇", selects);

List<String> selects1 = new ArrayList<>();
Collections.addAll(selects1, "B", "C" , "D");
data.put("胡涛", selects1);

List<String> selects2 = new ArrayList<>();
Collections.addAll(selects2 , "A",  "B", "C" , "D");
data.put("刘军", selects2);

System.out.println(data);

// 3、统计每个景点选择的人数。
Map<String, Integer> infos = new HashMap<>(); // {}

// 4、提取所有人选择的景点的信息。
Collection<List<String>> values = data.values();
System.out.println(values);
// values = [[A, B, C, D], [B, C, D], [A, C]]
//             value

for (List<String> value : values) {
    for (String s : value) {
        // 有没有包含这个景点
        if(infos.containsKey(s)){
            infos.put(s, infos.get(s) + 1);
        }else {
            infos.put(s , 1);
        }
    }
}

System.out.println(infos);

不可变集合

// 1、不可变的List集合
List<String> strings = Arrays.asList("sdf", "sdf");

List<Double> lists = List.of(569.5, 700.5, 523.0,  570.5);
// lists.add(689.0);
// lists.set(2, 698.5);
// System.out.println(lists);
double score = lists.get(1);
System.out.println(score);

// 2、不可变的Set集合
Set<String> names = Set.of("迪丽热巴", "迪丽热九", "马尔扎哈", "卡尔眨巴" );
// names.add("三少爷");
System.out.println(names);

// 3、不可变的Map集合
Map<String, Integer> maps = Map.of("huawei",2, "Java开发", 1 , "手表", 1);
// maps.put("衣服", 3);
System.out.println(maps);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值