Java 集合

一.java 集合框架

1.1 将集合的接口与实现分离

简单地定义接口    每一个实现(循环数组或链表)都可以通过一个实现了Queue接口的类表示

可以使用接口类型存放集合的应用:Queue<Customer> expressLane = new CircularArrayQueue<>(100);

1.2 Collection接口 :集合类的基本接口是Collection接口,这个接口有两个基本方法:

boolean add(E element);
Iterator<E> iterator();
...

add方法用于向集合添加元素,如果改变了集合就返回true,否则返回false。

iterator方法用于返回一个实现了Iterator接口的对象,可以使用这个迭代器对象依次访问集合中的元素。

1.3 iterator迭代器

Iterator接口包含4个方法,E为参数,E next(); boolean hasNext(); void remove(); default void forEachRemaining(Consumer<?  super E> action);

next 方法反复调用可以逐个访问集合中的每个元素,但是为了确定有没有到达集合的末尾,需要在调用next之前调用hasNext方法。

如果迭代器对象还有多个供访问的元素,hasNext就返回ture。 遍历数组和字符串可以用for each循环效率更高。

java迭代器查找一个元素的唯一方法是调用next,在执行查找操作时,迭代器的位置随之向前移动,返回刚刚越过的那个元素的引用。

Iterator接口的remove()方法将会删除上次调用next()方法时返回的元素,因此,如果要删除一个元素,首先需要使用一次next()方法查找。

1.4 泛型实用方法

由于Collection与Iterator都是泛型接口,可以编写操作任何集合类型的实用方法。例如下面一个检测任意集合是否包含指定元素的泛型方法:

public static <E> boolean contains(Collection<E> c, Object obj)
{
    for(E element : c)
        if (element.equals(obj))
            returm true;
     return false;
}

二. 具体的集合

Java类库中的集合有例如ArrayList,LinkedList等多种类型,除了以Map结尾的类,其他类都实现了Collection接口。

2.1 链表

与数组在中间删除或插入一个元素需要付出很大的代价相比,链表中间插入删除明显简单很多,Java中所有链表实际上都是双向链接的。

从链表中删除一个元素,只需要更新被删除元素附近的链接。

在元素无序的情况下,只对自然有序的集合使用的Iterator接口就没有add方法,集合类库提供了子接口ListIterator,其中包含add方法

interface ListIterator<E> extends Iterator<E>
{
    void add(E element);
    ...
}

这个方法不返回boolean类型的值,且有两个方法可以用来反向遍历链表。

E previous()

boolean hasPrevious()

 set方法用新元素取代next或previous方法返回的上一个元素。

为了避免并发修改的异常,需要遵循规则:更具需要给容器附加许多的迭代器,但是这些迭代器只能读取列表。另外,再单独附加一个既能读又能写的迭代器。

get方法可以查看链表第n个元素,但是必须从头开始越过n-1个元素,因此需要采用整数索引访问元素时,通常不采用链表。

2.3 散列表  (二叉查找树)

有几种能够快速查找元素的数据结构,其缺点是无法控制元素出现的次序。

散列表(hash table)就是可以快速地查找所需要对象的数据结构,它为每个对象计算一个称为散列码(hash code)的整数,散列码由对象的实例域产生。

自定义类需要自己实现这个类的 hash code,同时要与equals的定义一致,如果(x)equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。

散列值计算:每个列表被称为桶(bucket), 首先算出散列码,索引计算方式:“散列码/桶(bucket)总数 余 N” ,如果第N号桶没有其他元素,就可以将元素直接插入到桶中就可以了。

再散列:如果装填因子为0.75(默认值),这个表就会用双倍的桶数自动进行再散列,即创建一个双倍桶数的新表,并将所有的元素插入到新表中,然后丢弃原来的表。

实现散列表:Java集合类库提供了一个HashSet类,可以实现基于散列表的集,可以用add方法添加元素。

2.4 树集

TreeSet类:任意顺序将元素插入到集合中,在对集合进行遍历时,每个值将自动按照排序后的顺序呈现,与散列集相似但有所改进。

TreeSet类的特点:使用树结构排序,每次将一个元素添加到树中时,都被放置在正确的排序位置上,迭代器以排好序的顺序访问每个元素。

优点:查找新元素的正确位置平均需要 log₂n 次比较,部分情况快于数组或散列。  缺点:将元素添加到树中要比添加到散列表中慢。

实现树集:TreeSet()方法 

注:要使用树集,必须实现Comparable 接口,或者构造集时必须提供一个Comparator

2.5 队列与双端队列

特点:有两个端头的队列,可以有效得在队列的头部和尾部同时添加或删除元素,不支持在队列中添加元素。

实现:Deque接口,ArrayDeque和LinkedList类实现。

2.6 优先级队列

优先级队列:元素可以按照任意的顺序插入,却总是按照排序的顺序进行检索。

优先级队列特点:使用堆( heap )数据结构,这是一个可以自我调整的二叉树。

典例:任务调度:任务以随机乱序加到队列中,每当启动一个新的任务,都会将优先级最高的任务从队列中删除(1一般设为最高优先级,即把最小的元素删除)

三.映射

映射(map): 映射是种数据结构,映射用来存放键/值对,如果提供了键,就能查找到值

3.1  基本映射操作

映射实现:HashMap 和 TreeMap ,是两个通用的实现

操作过程:首先将键/值对添加到映射中,然后,从映射中删除一个键,同时与之对应的值也被删除了;修改某一个键对应的值,并调用get方法查看这个值

3.2 更新映射项

更新映射项:正常情况下,可以得到与一个键关联的原值,完成更新,再放回更新后的值。不过,必须考虑一个特殊情况,即键第一次出现。

3.3 映射视图

映射的视图(view)——这是实现了Collection 接口或某个子接口的对象

keySet();  values();   entrySet();三种视图分别为键值,值集合,以及键/值对集

3.4 弱散列映射

WeakHashMap:使用弱引用保存键。作用当某个对象只能由WeakReference引用,WeakHashMap周期性检查队列,当一个弱应用进入队列,意味着这个键不再被使用,于是删除对应条

3.5 链接散列集与映射

LinkedHashSet和LinkedHashMap类用来记住插入元素项的顺序

3.6 枚举集与映射

EnumSet:一个枚举类型元素集的高效实现,没有公共的构造体,可以使用静态工厂的方法构造这个集

EnumMap:是一个键类型为枚举类型的映射

3.7 标识散列映射

类IdentituHashMap :类生成hashCode的方法是根据内存地址计算散列码,因此使用 == 不使用 equals。

特点:不同的键对象,即使内容相同,仍然被认为是不同的对象。在实现对象遍历算法时,可以用来跟踪每个对象的遍历情况。

四. 视图与包装器

视图(views) :可以获得其他的实现了Collection接口和Map接口的对象

例如:映射类的KeySet方法,KeySet方法返回一个实现Set接口的类对象,这个类的方法对原映射进行操作。

4.1 轻量级集合包装器

 java中的视图,可以说其实就是一个具有限制的集合对象,只不过这里的不是集合对象,而是一个视图对象。例如:这里有一个Test类

Test[] tests = new Test[10];
List<Test> testList = Arrays.asList(tests);
  这里的testList是一个视图对象,具有访问数组元素set,get的方法。但是如果调用改变数组的方法就会抛出异常。所以可以说视图对象可以说是具有限制的集合对象。
4.2 子范围

子范围(subrange)视图 ,例如将一个列表staff,从中取出第10~19个元素,可以使用subList方法来获得一个列表的子范围视图

4.3 不可修改的视图

Collections.unmodiifiableCollection 等方法:对现有集合增加一个运行时的检查,如果发现视图对集合进行修改,就抛出一个异常。同时保持集合为未修改的状态。

4.4 同步视图

为了保证多个线程访问集合时,集合不会被意外地破坏,因此,使用视图机制确保常规集合的线程安全。

Collection类的静态synchronizedMap 方法可以将任何一个映射表转换成具有同步访问方法的Map。

4.5 受查视图

受查视图:用来对泛型类型发生问题时提供调试支持

例子:List<String> safeStrings = Collection.checkedList(strings,string.class);

五.算法

泛型集合接口的优点:算法只需要实现一次

例如: public static <T extends Comparable> T max(Collection<T> c)  //max方法实现为能够接收任何实现了Collection接口的对象

5.1 排序与混排

Collections类中的sort方法:可以对实现了List接口的集合进行排序

Collections类中的shuffle方法:随机的混排列表中元素的顺序

5.2 二分查找

二分查找:直接查看位于数组中间的元素,看一看是否大于要查找的元素,如果是,则用同样的方法在数组前半部分查找。不是,则在后半部分用同样的方法

优点:快捷,如果有1024个元素,可以在10次比较后定位元素(或者确认查找的元素在数组中是否存在),而线性查找需要比较512次,不存在时需要1024次。

Collections类的binarySearch方法:实现了这个算法,注意提供的集合必须是排好序的,否则算法将返回错误的答案。

注意:如果为binarySearch算法提供一个链表,它会自动地变为线性查找。

5.3 简单算法

Collection类中包含了几个简单且有用的算法,例如查找集合中最大元素的示例,这是为了方便编写和阅读代码

5.4 批操作

x.removeAll(y); x.retainAll(y);等

5.5 集合与数组的转换

Arrays.asList 包装器:可以将一个数组转换为集合

String[] values = ...;
HashSet<String> staff = new HashSet<>(Array.asList(values));

toArray 方法:从集合得到数组

但是toArray方法返回的数组是一个Object[]数组,不能使用强制类型转换。因此,必须使用toArray方法的一个变体形式:

    //提供一个所需类型而且长度为0的数组,返回的数组会创建为相同的数组类型
       String[] values = staff.toArray(new String[0]);
        //可以构造一个指定大小的数组
        staff.toArray(new String[staff.size()]);

 5.6 编写自己的算法

编写自己的算法(即以集合作为参数的任何方法):应该尽可能地使用接口,而不是具体的实现。

六.遗留的集合

①Hashtable类:与HashMap类的作用一样,对同步性或与遗留代码的兼容性没要求,就用HashMap,需要并发访问则用 ConcurrentHashMap。

②枚举:遗留集合使用Enumeration接口对元素序列进行遍历。

③属性映射(property map):具有三大特性:键与值都是字符串、表可以保存到一个文件中,也可以从文件中加载、使用一个默认的辅助表。

使用范围:实现属性映射的Java平台类称为Properties。 属性映射通常用于程序的特殊配置选项

④栈(Stack)类:有push方法和pop方法,且Stack类扩展为Vector类。

⑤位集 (BitSet类):用于存放一个位序列(实际上称它位向量或位数组更贴切)

特点:BitSet类提供了一个便于读取,设置或清除各个位的接口,且位集存储位序列时,比使用Boolean对象的ArrayList更加高效。

 

posted @   Pray386  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示