集合
数组有一定的缺陷
- 数组长度固定不变
- 可通过数组名.length获取数组的长度,去无法直接获取数组中存储元素的实际长度
- 数组申请空间是连续的
针对数组的缺陷,java提供了集合框架(位于java.util包中)
Iterable接口
此接口位于java.lang.Iterable中,是java标准库中的接口,用于表示可迭代的集合类,实现了Iterable接口的类可以使用java中的for-each
循环来遍历其中的元素,使其具有可迭代性。
Iterable中的方法
返回值类型 | 方法 | 描述 |
---|---|---|
void | forEach(Consumer<? super T> action) | 对Iterable对象的每个元素执行给定的操作,知道那个处理完所有元素或者引发异常 |
Iterable | iterator() | 返回迭代type T 的迭代器 |
实现了Iterable接口的类可以通过实现iterator()方法来返回一个迭代器对象,从而使for-each循环语法来遍历结合中的元素。
下面我们以ArrayList为例子来展示几个常见的错误
// 错误1
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for (int i = 0; i < list.size(); i++) {
//修改操作
list.remove(i);
//添加操作
//添加错误也会错误
}
System.out.println(list);
}
// 结果是 [b, d]
// 原因是size是动态变化的
// 错误2
//
for (String s:list)
{
list.add("d");
}
//
//错误三
Iterator iterator = list.iterator();
while (iterator.hasNext())
{
list.remove(iterator.next());
}
在上面的错误中,错误2和错误3同理都是因为不能在迭代器中使用**原对象
**进行删除.。
但是在迭代器对对象中,提供了一种方法。
迭代器对象提供了一种默认方法remove
,表示删除迭代器游标当前所指的对象。
Iterator iterator = list.iterator();
for (int i = 0; i < list.size(); i++) {
iterator.next();
//list.remove(iterator.next()); --报错
if ("a".equals(list.get(i)))
{
iterator.remove();
}
}
这样成功的删除了元素。
Collection接口
Collection接口内的方法
这里大部分是抽象方法,为后面的类提供规范,后面的派生类要实现这些基本的方法。
Collection 下面有三个接口
Set接口
- 实现了set接口的集合,其中的元素是唯一的。
- Set集合是不可变数据类型,不可以对其进行修改
- Set集合中不能使用null元素
//个别方法示例
public static void main(String[] args) {
//of 和copy是Set的静态方法
Set set = Set.of("dasd","ad","da","dadasdwa",'s');
//Set的一些方法
//toArray 将set集合转化成Object数组,无序
System.out.println(Arrays.toString(set.toArray()));
// contains 判断是否有指定字符或者字符序列 返回布尔值
System.out.println(set.contains('s'));
System.out.println(set.contains("da"));
// retainAll(Collection<?> c) 仅仅保留c中存在的元素 交集
/* Set set1 = Set.of('s');
System.out.println(set.retainAll(set1));*/
// 报错Set集合不可变
}
常用子类HashSet
和TreeSet
TreeSet
上面的结果只有a
s
w
三个元素,并且是无序的
HashSet
List接口
有序集合(也称为序列 )。 该集合用户可以精确控制列表中每个元素的插入位置。 用户 可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。
- List 通常允许重复的元素、允许 null 元素
- List是不可变的
public static void main(String[] args) {
List<String> list = List.of("a","c","d","f","s","a");
// List方法
// listIterator 返回原顺序的迭代器对象,可传参,返回指定位置开始的迭代器对象
Iterator iterator = list.listIterator(2);
while (iterator.hasNext())
{
System.out.println(iterator.next());
}
//replaceAll在List中不能用
/* list.replaceAll(t->t+"s");
System.out.println(list);*/
// List不可变
/* list.add("s");
System.out.println(list);*/
}
Vector
public static void main(String[] args) {
List<String> list = List.of("a","c","d","f","s","a");
Vector vector = new Vector();
vector.addAll(list);
System.out.println(vector.get(0));
// 转换器
vector.replaceAll(o-> "字母是:" + o);
System.out.println(vector);
List a = vector.subList(0,3);
a.add("a");
System.out.println(a);
System.out.println(vector);
// 结果是
// a
//[字母是:a, 字母是:c, 字母是:d, 字母是:f, 字母是:s, 字母是:a]
//[字母是:a, 字母是:c, 字母是:d, a]
//[字母是:a, 字母是:c, 字母是:d, a, 字母是:f, 字母是:s, 字母是:a]
}
!!!注意subList等方法生成的子视图,会对原来的集合产生影响
Stack
和之前写的栈数据类型相似的操作
Queue接口
实现了单链表
下面有Deque(实现了双链表)接口
LinkedList
抛出异常 | 返回特殊值 | |
---|---|---|
插入 | add | offer |
删除 | remove | poll |
检查 | element | peek |
AbstractCollection抽象类
具有Collection的性质
Map
- Map 接口不是 Collection 的子接口,使用键、值映射表来存储
- Map 不能有重复的键(覆盖),每个键可以映射到最多一个值
- 允许将映射内容视为一组键、值集合或键值映射集合
- key 不要求有序,不可以重复。 value 也不要求有序,但可以重复
- 当使用对象作为 key 时,要重写 equals 和 hashCode 方法
//创建不可修改的Map对象
1. Map.of
2. Map.ofEntries
3. Map.copyOf
这三种方式创建的是不可修改的
Map的基本方法
TreeMap
- 继承 AbstractMap ,一个红黑树基于 NavigableMap 实现
- 非线程安全的
- key 不能存 `null ,但是 value 可以存 null
- key 必须是可比较的 (实现 Comparable 接口,传递一个 Comparator 比较器)
Map map1 = Map.of(1,"a",2,"b",5,"s");
map1.remove(1);
// 报错,这样定义的Map对象是不能改变的
Map map = new TreeMap();
map.put("鸡","蔡徐坤");
map.put("鸡1","蔡虚坤");
map.put("鸡5","蔡虚鲲");
map.put("鸡3","蔡坤坤");
map.put("鸡4","蔡坤坤");
System.out.println(map);
// 结果是 {鸡=蔡徐坤, 鸡1=蔡虚坤, 鸡3=蔡坤坤, 鸡4=蔡坤坤, 鸡5=蔡虚鲲}
// TreeMap 定义的是有序的是根据键的自然排序放入的
// 实现了compable接口
遍历Map
// 遍历map集合
// 1.通过entrySet()方法
for (Map.Entry entry: map.entrySet())
{
System.out.println(entry.getKey() + " = " + entry.getValue());
}
// 2.通过 KeySet()方法
for (String key: map.keySet())
{
System.out.println(key + " = " + map.get(key));
}
HashTable
- 该类实现了一个哈希表,它将键映射到值 不允许 null 作为键和值
- 默认初始容量( initialCapacity )为 11 ,默认负载因子( loadFactor )为 0.75f
- 同步的(线程安全的)
- 不保证顺序
- 扩容方式是旧容量的2倍 +1
- 为什么hashtable的扩容方式选择为2n+1 为了均匀分布,降低冲突率
- 数组 + 链表方式存储实现Hash表存储
- 添加值时 如果 hash 一样 equals 为 false 则将当前值添加到链表头 如果 hash 一样 equals 为 true 则使用当前值替换原来的值 ( key 相同)
public static void main(String[] args) {
Hashtable table = new Hashtable();
table.put("a","蔡徐坤");
table.put("c","蔡虚坤");
table.put("w","蔡虚鲲");
table.put("t","蔡坤坤");
table.put("y","蔡坤坤");
// 具有和迭代器相同的作用,但是不具备迭代器的删除功能
Enumeration enumeration = table.keys();
System.out.println(table);
}
HashMap
- 基于哈希表的实现Map接口
- 允许
null
值的键和值 - 非线程安全
- 默认容量是16,负载因子0.75
- 扩容是2倍旧的容量
- 在存储数据时,key的hash计算调用的是HashMap中的hash方法
- 添加值时,如果hash一样,添加到链表的尾部
蔡坤坤");
// 具有和迭代器相同的作用,但是不具备迭代器的删除功能
Enumeration enumeration = table.keys();
System.out.println(table);
}
## HashMap
- 基于哈希表的实现Map接口
- 允许`null`值的键和值
- 非线程安全
- 默认容量是16,负载因子0.75
- 扩容是2倍旧的容量
- 在存储数据时,key的hash计算调用的是HashMap中的hash方法
- 添加值时,如果hash一样,添加到链表的尾部
[外链图片转存中...(img-hO4ir6Oo-1694017456510)]