集合详解(详解)

1、为什么要使用集合

在没有学习集合之前,保存多个数据,一般都使用数组,那么数组有哪些不足的地方呢?集合相对于数组又有哪些好处呢?

数组的不足之处:

  1. 数组的长度在一开始就必须指定,而且一旦指定,就不能修改
  2. 保存的必须是同一类型的元素
  3. 使用数组进行增删元素是比较麻烦

集合相较于数组的好处:

  1. 可以动态保存任意多个对象,使用比较方便
  2. 提供了一系列方便操作对象的方法:add、remove、set、get等,后续细说。
  3. 使用集合添加、删除新的元素比较简洁

2、集合的框架体系

Java的集合类有很多,主要分为两大类 :如图

在这里插入图片描述

在这里插入图片描述

Collection主要有两个子接口 List 和 Set 也就是所谓的单列集合

Map接口的实现子类是双列集合,也就是存放的键值对(K - V) 双列集合

3、Collection接口及其实现类

Collection的子接口主要是 List 和 Set

3.1、List接口的和其常用方法

List实现子类的特点:

  1. List集合类中元素有序(即添加顺序和取出顺序一致)、 且可重复
  2. List集合中的每个元素都有相对应的顺序索引,支持索引
  3. List中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据其序号存取容器中对应的元素。

List接口实现类ArrayList和LinkedList

linkedList的用法和ArrayList的用法基本一致。在后续区别见LinkedList的特有方法

  • ArrayList的特点
//List集合类中元素有序(即添加顺序和取出顺序一致)、 且可重复
List list = new ArrayList();
        list.add(0);
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(4);
        list.add(null);
		//输出为:list= [0, 1, 2, 3, 4, 4, null]
        System.out.println("list= " + list);
//List集合中的每个元素都有相对应的顺序索引,支持索引,索引从零开始
	System.out.println("第三号元素为: " + list.get(3));//输出 3
  • ArrayList实现类的常用方法
 List list = new ArrayList();
        list.add("红楼梦");
        list.add("水浒传");
        list.add("三国演义");
        list.add("西游记");
        //list= [红楼梦, 水浒传, 三国演义, 西游记]
        System.out.println("list= " + list);

//1.add(int index , Object ele) 在index位置插入ele元素,原来位置及后续元素依次后移一位
        list.add(1,"曹雪芹");
        //list= [红楼梦, 曹雪芹, 水浒传, 三国演义, 西游记]
        System.out.println("list= " + list);

//2.addAll(int index , Collection eles);从index位置开始,把eles全部添加到集合,原来位置及后续元素依次后移
        List list1= new ArrayList();
        list1.add(9);
        list1.add(8);
        list1.add(7);
        list.addAll(1,list1);
        //list=[红楼梦, 9, 8, 7, 曹雪芹, 水浒传, 三国演义, 西游记]
        System.out.println("list=" + list);

//3.get(int index) 获取指定index位置的元素
        System.out.println(list.get(6));//输出 三国演义

//4.indexOf(Object obj)返回obj在集合中首次出现的位置
        System.out.println(list.indexOf("三国演义"));//输出 6
        // lastIndexOf()  返回元素在集合中最后出现的位置

//5.remove(int index) 移除指定位置的元素,并返回此元素
        System.out.println(list.remove(0));//红楼梦
        //list= [9, 8, 7, 曹雪芹, 水浒传, 三国演义, 西游记]
        System.out.println("list= " + list);

//6.set(int index , Object ele) 设置指定位置的元素为ele,相当于替换
        list.set(1,"三国演义");
        //list= [9, 三国演义, 7, 曹雪芹, 水浒传, 三国演义, 西游记]
        System.out.println("list= " + list);
//7.subList() 截取子列表 左闭右开
		List list1 = list.subList(1, 3);

//8.toArray() 将集合转换为数组
		Object[] objects = list.toArray();
		System.out.println("数组的长度为:" + objects.length);
//Arrays.asList() 将数组转换为集合
		List obj = Arrays.asList(objects);

//9.clear() 清除列表所有数据
		list.clear();

//10.isEmpty() 判断列表是否为空(没有元素)  空返回True 否则返回false
		System.out.println("是否为空:"+list.isEmpty());

//11.contains() 判断集合中是否存在某个字符串
System.out.println("判断是否包含abc:" + list.contains("三国演义"));

//12.size()获取集合中存了几个数据。
		System.out.println(list.size());

ArrayList和LinkedList的区别

ArrayList在于底层用的实现方法是数组,而LinkedList使用的是双向链表

这就使得LinkedList在增删上快于ArrayList ,ArrayList的读写快于LinkedList

且其使用双向链表就就使得其有部分的方法不同于Arraylist的方法

  • ArrayList的底层逻辑(不追源码,只浅谈)
  1. ArrayList中维护了一个Object类型的数组elementData.

  2. 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1
    次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1。5倍。

  3. 如果使用的是指定大小的构造器,则初始elementData容量为指定大小, 如果需要扩容
    则直接扩容elementData为1.5倍。

  • LinkedList’的底层逻辑
  1. LinkedList底层维护了一个双向链表
  2. LinkedList中维护了两个属性first和last分别指向首节点和尾节点每个节点(Node对象) ,里面又维护了prev、next、 item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表.
  3. 所以LinkedList的元素的添加和删除, 不是通过数组完成的,相对来说效率较高。
  • LinkedList特殊方法
LinkedList list = new LinkedList();
list.add(5);
list.add(7);
list.add(9);
//在第一个位置和最后一个位置插入数据
list.addFirst(1);
list.addLast(9);

//获取第一个位置和最后一个位子的数据
System.out.println(list.getFirst());
System.out.println(list.getLast());

//删除第一个节点 和最后一个节点
list.removeFirst();
list.removeLast();

//pop() 出栈 弹出第一个节点(会从列表中删除)
System.out.println("弹出元素:" + list.pop());

//push() 入栈(压栈) 在第一的位子插入一个数
list.push(3);

3.2、Set接口的实现类及其常用方法

Set实现类的特点:

  1. 无序(添加和取出的顺序不一致),没有索引
  2. 不允许重复元素,所以最多包含一个null
Set set = new HashSet();
        set.add("三国演义");
        set.add("水浒传");
        set.add("红楼梦");
        set.add("西游记");
        set.add("红楼梦");//重复
        set.add("三国演义");//重复
        set.add(null);
        set.add(null);//再次添加 null
        //set=[null, 水浒传, 三国演义, 红楼梦, 西游记]
        System.out.println("set=" + set);

Set接口实现类TreeSet和HashSet

和 List 接口一样, Set 接口也是 Collection 的子接口,因此,常用方法和 Collection 接口一样.

只有部分方法在Set中没有,如:Set没有下标,所以没有get()方法。

  • Set实现类的方法演示
//创建HashSet,默认长度为16 加载因子为0.75
Set<String> set = new HashSet<>();
//往set添加元素
set.add("Java");
set.add("Html");
set.add("SQL");
set.add("Java");
System.out.println(set.size());
//contains() 判断是否包含某个对象
// 自己写的类,需要去重写hashcode和equals
set.contains("java");
//remove()删除集合的内容
set.remove("Java");
//clear() 清空set集合 
set.clear();
//读取set的元素  只能遍历
for (String str: set) {
    System.out.println(str);
}
}
  • TreeSet的部分方法
TreeSet<Integer> set = new TreeSet<>();
set.add(20);
set.add(25);
set.add(8);
set.add(10);
set.add(16);
set.add(18);

//first() 返回第一个元素(排序之后)
set.first();

//last() 返回最后一个元素 排序之后的
set.last();

//ceiling() 返回比指定元素大的最小元素
System.out.println(set.ceiling(19));//20

//floor() 返回比指定元素小的最大元素
System.out.println(set.floor(9));//8

//size()返回元素个数
System.out.println("元素的个数:" + set.size());

//remove() 删除元素,根据类容来删
set.remove(18);

//clear() 清空所有元素
set.clear();

//isEmpty() 判断集合是否为空
set.isEmpty();

//遍历是按照元素排序显示
for (Integer i : set) {
    System.out.println(i);
}

TreeSet和HashSet的区别

TreeSet采用红黑树数据结构实现的。

不能添加null ,元素有序(添加的元素会自动排序),遍历出来是按照从小到大的顺序排列的。不能添加重复元素

TreeSet的元素必须要实现Comparable 可以比较两个对象的大小

HashSet使用的是哈希表来存储数据。

  • TreeSet实现Comparable来比较大小的步骤
  1. 实现Comparable接口,重写方法 compare方法
  2. 方法对象的参数大于参数对象返回 1 ,如果要降序,就返回-1
  3. 当前对象与参数对象小,就返回 -1,降序返回 1
  4. 当前对象与参数对象相等,返回0。

4、List和Set的遍历方式

  • List的三种遍历方式[ArrayList ,LinkedList] 和 Set[TreeSet,HashSet]的两种遍历方式。
  1. 迭代器遍历(iterator) ---- 可用于List和Set
//list左迭代遍历
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(5);
    list.add(15);
    list.add(66);
    list.add(88);

    //返回迭代器,迭代器里面存有数据
    Iterator<Integer> iterator = list.iterator();
    //hashNext()方法判断里面是否还有数据
    //快捷生成下面遍历 itit
    while (iterator.hasNext()){
        //next()从迭代器中取出一个数据,去一个就减少一个
        Integer next = iterator.next();
        System.out.println(next);
  1. 增强for遍历(foreach) -----可用于List和Set
List list = new ArrayList();
        list.add(0);
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        //Object 集合内存储的类型 ,o 代表遍历出来的对象 
        // list是取出数据的位置,循环一次就取出一个数据放入o内
        for (Object o : list) {
            System.out.println(o);
        }
  1. 普通for循环 —可用于List 因为Set接口没有get方法,不能使用普通for遍历
List list = new ArrayList();
        list.add(0);
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

5、双列集合Map

1.Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value

2.Map中的key和value 可以是任何引用类型的数据,

3.Map中的key不允许重复 Map中的value可以重复

4.Map的key可以为null, value也可以为null,注意key为null,只能有一个,
value为null ,可以多个。常用String类作为Map的key

5.key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value

语法: 以HashMap为例

//<String,String> 两个String表示键和值的类型,可以是其他数据类型
Map<String,String> map = new HashMap<>();

Map的实现类主要有两个 HashMap 和 TreeMap,使用方法基本一致,以HashMap举例

Map map = new HashMap();
map.put("no2", "张无忌");//k-v
map.put("no1", "张三丰");//当有相同的 k , 就等价于替换. map.put("no3", "张三丰");//k-v
map.put(null, null); //k-v
map.put(null, "abc"); //等价替换
map.put("no4", null); //k-v
map.put("no5", null); //k-v
map.put(1, "赵敏");//k-v
map.put(new Object(), "金毛狮王");//k-v
// 通过 get 方法,传入 key ,会返回对应的 value
System.out.println(map.get("no2"));//张无忌
System.out.println("map=" + map)
  • Map接口的方法
Map map = new HashMap();
//put() 往Map中添加数据,第一个参数为键 第二个参数为值
map.put("中国移动","10086");
map.put("中国联通", "10010");
map.put("中国电信", "10001");
map.put("中国移动", "100866");//替换
map.put("红楼梦", "曹雪芹");
map.put("西游记", "吴承恩");
map.put("三国演义", "罗贯中");
//map={中国移动=100866, 中国电信=10001, 红楼梦=曹雪芹, 三国演义=罗贯中, 西游记=吴承恩, 中国联通=10010}
System.out.println("map=" + map);

 // remove:根据键删除映射关系
        map.remove("三国演义");
        System.out.println("map=" + map);

// get:根据键获取值
        Object val = map.get("中国移动");
        System.out.println("val=" + val);//val = 100866

// size:获取元素个数
		System.out.println("k-v=" + map.size());

// isEmpty:判断个数是否为 0
		System.out.println(map.isEmpty());//F

// clear:清除 k-v
		map.clear();
		System.out.println("map=" + map);

// containsKey:查找键是否存在
System.out.println("结果=" + map.containsKey("中国移动"));//T

Map的遍历方式

Map map = new HashMap();
map.put("中国移动","10086");
map.put("中国联通", "10010");
map.put("中国电信", "10001");
map.put("中国移动", "100866");//替换
map.put("红楼梦", "曹雪芹");
map.put("西游记", "吴承恩");
map.put("三国演义", "罗贯中");

//第一组: 先取出 所有的 Key , 通过 Key 取出对应的 Value
Set keyset = map.keySet()
//(1) 增强 for
System.out.println("-----第一种方式-------");
for (Object key : keyset) {
System.out.println(key + "-" + map.get(key));
}
//(2) 迭代器
System.out.println("----第二种方式--------");
//获取迭代器
Iterator iterator = keyset.iterator();
//使用迭代器方法进行遍历,当数据取完时结束
while (iterator.hasNext()) {
    //获取下一个数据
Object key = iterator.next();
System.out.println(key + "-" + map.get(key));
}


//第二组: 把所有的 values 取出
Collection values = map.values();
//这里可以使用所有的 Collections 使用的遍历方法
//(1) 增强 for
System.out.println("---取出所有的 value 增强 for----");
for (Object value : values) {
System.out.println(value);
}
//(2) 迭代器
System.out.println("---取出所有的 value 迭代器----");
Iterator iterator2 = values.iterator();
while (iterator2.hasNext()) {
Object value = iterator2.next();
System.out.println(value);
}


//第三组: 通过 EntrySet 来获取 k-v
Set entrySet = map.entrySet();
//(1) 增强 for
System.out.println("----使用 EntrySet 的 for 增强(第 3 种)----");
for (Object entry : entrySet) {
//将 entry 转成 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
//(2) 迭代器
System.out.println("----使用 EntrySet 的 迭代器(第 4 种)----");
Iterator iterator3 = entrySet.iterator();
while (iterator3.hasNext()) {
Object entry = iterator3.next();
//向下转型 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}

开发中如何选择使用那种集合

在开发中,选择什么集合实现类,主要取决于业务操作特点,然后根据集合实现类特性进行
选择,分析如下:

1先判断存储的类型(- 组对象[单列]或一-组键值对[双列])

2一组对象[单列]: Collection接口

允许重复: List

增删多: LinkedList [底层维护了一个双向链表]

改查多: ArrayList [底层维护Object类型的可变数组]

不允许重复: Set

无序HashSet [底层是HashMap ,维护了一个哈希表即(数组+链表+红黑树)]

排序: TreeSet

插入和取出顺序一致: LinkedHashSet ,维护数组+双向链表

3一组键值对[双列]: Map

键无序HashMap [底层是:哈希表jdk7: 数组+链表,jdk8: 数组+链表+红黑树]

键排序: TreeMap
键插入和取出顺序一致: LinkedHashMap

读取文件Properties

有部分集合未在本文中提及,但用法差不多一致

6、Collections 工具类

1.Collections是一个操作Set、 List和Map等集合的工具类

2.Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作

Collections的常用方法

  • 排序方法
  1. reverse(List): 反转List中元素的顺序

  2. shuffle(List):对List集合元素进行随机排序

  3. sort(List):根据元素的自然顺序对指定List集合元素按升序排序

  4. sort(List, Comparator): 根据指定的Comparator产生的顺序对List集合元素进行排序

  5. swap(List, int, int): 将指定list 集合中的i处元素和j处元素进行交换

  • 查找、替换方法
  1. Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素

  2. Object max(Collection, Comparator): 根据Comparator指定的顺序

  3. 返回给定集合中的最大元素Object min(Collection)

    Object min(Collection, Comparator) --最小元素

  4. int frequency(Collection, Object): 返回指定集合中指定元素的出现次数

  5. void copy(List dest,List src):将src中的内容复制到dest中

  6. boolean replaceAll(List list, object oldVal, Object newVal):使用新值
    替换List对象的所有旧值

以上方法均为静态方法,直接使用Collections.方法名() 即可调用

7、泛型集合

在集合的定义时,指定集合内存放的数据格式,可以是自己写的类,如果编译器发现添加的类型,不满足要求,就会报错。而在遍历时,可以直接取出该类型(不添加泛型时,取出的是Object)

  • 使用泛型的好处:

    1.编译时,检查添加的元素类型,提高了安全性

    2.减少了类型的转换次数,提高效率

    3.不在提示编译警告

语法:

//此处指定了String,在存放时就必须存放字符串,否则会报错误
List<String> strList = new ArrayList<> ();
  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值