java基础

Wesley13
• 阅读 519

JAVA 集合

在处理数据的过程中经常会需要一个容器来存储某一类型的数据,Java 中的数组就是这样一种容器。但 Java 中的数组有其局限性,定义后的数组长度不可变,超出数组长度后就不能再存放数据了。而很多时候我们并不知道数据到底有多少,所以就需要有不定长的容器来存放数据,这就是集合,Java 中的集合都采用了泛型实现,可以存入任何类型的对象数据。
Java 中的数组:

12345678910111213141516

package test;import java.util.Arrays;public class Arr{    public static void main(String[] args)    {        int[] arr = new int[2];        arr[0] = 1;        arr[1] = 2;        // arr[2] = 3; // 编译出错        System.out.println(Arrays.toString(arr)); // 输出:[1, 2]    }}

Java 中的集合主要分为四类:

  1. List 列表,有序,可重复
  2. Queue 队列,有序,可重复
  3. Set 集合,不可重复
  4. Map 映射,无序,键唯一,值不唯一

每种集合类型下都包含多个具体的实现类,如:
java基础

1. List 列表,有序、可重复

常用的 List 实现类有:ArrayList、LinkedList、Vector、Stack。

1.1 ArrayList 列表

ArrayList 数组列表,有序,可重复,内部是通过 Array 实现。
初始化对象时,如果没有传大小,则列表的大小为 DEFAULT_CAPACITY 的默认值 10。当列表容量不够时,继续往列表中追加元素,则通过数组拷贝,对原数组进行扩容,扩容的方式为 int newCapacity = oldCapacity + (oldCapacity >> 1),即新数组容量 newCapacity10 + 10/2 = 15。如果一次性追加多个元素时比如 6 个,这时候列表最小容量 minCapacity 需要 10 + 6 = 16,新的容量 newCapacity 小于最小容量 minCapacity 则新数组容量取最小容量值 newCapacity = minCapacity
对数组列表进行插入、删除操作时都需要对数组进行拷贝并重排序。所以如果能知道大概存储多少数据时,尽量初始化初始容量,提升性能。

12345

List a = new ArrayList();a.add(11);a.add("aaa"); // 添加元素a.get(1);     // 获取第二个元素值a.remove(1);  // 删除第 2 个元素

1.2 LinkedList 双向链表

LinkedList 是双向链表,也即每个元素都有指向前后元素的指针。既然是链表那么顺序读取的效率非常高,而随机读取的效率较低。当随机获取一个 index 位元素时,链表先比较 index 和链表长度 1/2 的大小,小于时从链表头部查找元素,大于时就从链表尾部查找元素。

123456

LinkedList l =new LinkedList();l.add(1);l.add(2);l.getFirst(); // 获取第一个元素l.getLast();  // 获取最后一个元素l.remove(1); // 删除第 2 个元素

对比 ArrayList 如果随机读取数据较多时使用 ArrayList 性能高,插入删除较多时使用 LinkedList 性能高。

1.3 Vector 向量,线程安全的列表

与 ArrayList 一样也是通过数组实现的,不同的是 Vector 是线程安全的,也即同一时间下只能有一个线程访问 Vector,线程安全的同时带来了性能的耗损,所以一般都使用 ArrayList。Vector 的扩容也与 ArrayList 不同,可以设置扩容值,默认每次扩容原来的一倍。

12345

Vector v = new Vector();v.add("aa");v.add("bb");v.get(1);v.remove(1);

1.4 Stack 栈,后进先出(LIFO)

Stack 继承自 Vector 所以也是数组实现的,线程安全的栈。因为 Stack 继承自 Vector 所以就拥有 Vector 中定义的方法,但作为栈数据类型,不建议使用 Vector 中与栈无关的方法,尽量只用 Stack 中的定义的栈相关方法,这样不会破坏栈数据类型。

12345

Stack s = new Stack();s.push(1);s.push(2);s.pop(); // 抛出并删除首个元素s.peek(); // 返回首个元素值,不删除值

1.5 ArrayQueue 数组队列,先进后出(FIFO)

ArrayQueue 是数组实现的队列,从队尾加入数据,只能队头删除数据,可随机读取队列数据。

123456

ArrayQueue q = new ArrayQueue(12);q.add(1);q.add(2);q.get(1);q.size();、q.remove(0);

2. Queue 队列,有序、可重复

继承自 Queue 的队列有:ArrayDeque、LinkedList、PriorityQueue。

2.1 ArrayDeque 数组实现的双端队列

ArrayDeque 是队列,但也可以作为栈使用,而且对比 Stack 更高效。作为双端队列那就可以在队列两端插入和删除元素。当追加元素超过容量限制时,则创建一个两边容量的新数组,并将原数组的内容拷贝到新数组中。

123456789

ArrayDeque d = new ArrayDeque();d.addFirst(11);d.addLast(22);d.pollFirst();d.pollLast(); // 返回并删除队尾元素d.peekLast(); // 返回但不删除队尾元素d.peekFirst();d.push(44);d.pop();

2.2 LinkedList 队列也是双向链表

上文 1.2 中已经提过,这里就不赘述了。推荐使用 ArrayDeque。

2.3 PriorityQueue 优先队列,数组实现的二叉树

PriorityQueue 是一个完全二叉树实现的小顶堆(任意一个非叶子节点的权值,都不大于其左右子节点的权值)。

123456

PriorityQueue q = new PriorityQueue();q.offer(1);q.offer(2);q.offer(3); // 插入元素q.peek();   // 查看顶端元素q.poll();   // 返回并删除顶端元素

详细介绍地址:PriorityQueue

3. Map 映射/字典,无序,键值对,键唯一

常用的 Map 实现有:HashMap、TreeMap、LinkedHashMap

3.1 HashMap 哈希映射/字典

HashMap 就是 key->value 的键值对数据,key 是唯一的,而且 key 和 value 都可以为 null。HashMap 和 HashTable 相似,HashTable 实现了线程同步,在Object超类解析章节中简单介绍过 HashTable 的数据存储方式。
HashMap 是个无序的字典,遍历时不保证元素顺序。HashMap 创建时默认会设置初始容量大小(默认16),和装载因子(默认 0.75,扩充容量的阀值),装载因子 = 已存入元素个数 / 总容量大小。当然这两个值也可以手动设置。
HashMap 的数据存储结构如下图:
java基础
HashMap 当插入一个数据时,先对 key 值做 hash,用得到的值与容器的大小 n 减 1 做 & 运算得到桶的位置,即:i = (n - 1) & hash,i 就是桶的位置。在桶中查找有无元素,没有直接插入,有则比较元素 key 值是否相同,相同用新值替换。
桶的位置计算为什么是 (n - 1) & hash?先看 hash 值的计算:

12345

// hash() 函数返回一个整数static final int hash(Object key) {    int h;    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}

hash() 函数对 key 取值后返回一个整数。又因为 HashMap 的容量 n 大小始终为 2 的幂(默认为 16),那么 大专栏  Java基础-集合de>n - 1 的二进制始终是最高位为 1,其它位为 0 的数,如:10...0,这个数与整数做 & 运算就得到 hash / n 的余数,余数的取值范围在 0 ~ n-1,很巧妙的设计。相关源码,这里截取了部分:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899

public class HashMap<K,V> extends AbstractMap<K,V>    implements Map<K,V>, Cloneable, Serializable {        // 构造函数    public HashMap(int initialCapacity, float loadFactor) {        if (initialCapacity < 0)            throw new IllegalArgumentException("Illegal initial capacity: " +                                               initialCapacity);        if (initialCapacity > MAXIMUM_CAPACITY)            initialCapacity = MAXIMUM_CAPACITY;        if (loadFactor <= 0 || Float.isNaN(loadFactor))            throw new IllegalArgumentException("Illegal load factor: " +                                               loadFactor);        this.loadFactor = loadFactor;                // 扩容的阀值        this.threshold = tableSizeFor(initialCapacity);    }        /**     * 容器大小,返回 2 的幂     * Returns a power of two size for the given target capacity.     */    static final int tableSizeFor(int cap) {        int n = cap - 1;        n |= n >>> 1;        n |= n >>> 2;        n |= n >>> 4;        n |= n >>> 8;        n |= n >>> 16;        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;    }        // 插入元素    public V put(K key, V value) {        return putVal(hash(key), key, value, false, true);    }        /**     * // 实际插入元素的方法     * Implements Map.put and related methods.     *     * @param hash hash for key     * @param key the key     * @param value the value to put     * @param onlyIfAbsent if true, don't change existing value     * @param evict if false, the table is in creation mode.     * @return previous value, or null if none     */    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,                   boolean evict) {        Node<K,V>[] tab; Node<K,V> p; int n, i;        if ((tab = table) == null || (n = tab.length) == 0)            // resize 函数会设置扩容阀值            n = (tab = resize()).length;        if ((p = tab[i = (n - 1) & hash]) == null)            tab[i] = newNode(hash, key, value, null);        else {            Node<K,V> e; K k;            if (p.hash == hash &&                ((k = p.key) == key || (key != null && key.equals(k))))                e = p;            else if (p instanceof TreeNode)                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);            else {                for (int binCount = 0; ; ++binCount) {                    if ((e = p.next) == null) {                        p.next = newNode(hash, key, value, null);                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st                            treeifyBin(tab, hash);                        break;                    }                    if (e.hash == hash &&                        ((k = e.key) == key || (key != null && key.equals(k))))                        break;                    p = e;                }            }            if (e != null) { // existing mapping for key                V oldValue = e.value;                if (!onlyIfAbsent || oldValue == null)                    e.value = value;                afterNodeAccess(e);                return oldValue;            }        }        ++modCount;        if (++size > threshold)            resize();        afterNodeInsertion(evict);        return null;    }         // 取 hash     static final int hash(Object key) {        int h;        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);    }   }

3.2 TreeMap 红黑树实现的 key->value 容器,可排序

红黑树是一种自平衡二叉查找树,介绍
更多参考:
https://www.cnblogs.com/skywang12345/p/3245399.html
https://www.ibm.com/developerworks/cn/java/j-lo-tree/index.html

3.3 LinkedHashMap 链表映射/字典

LinkedHashMap 继承自 HashMap 所以具有 HashMap 的所有特性。同时又实现了双向链表的特性,保留了元素插入顺序。

12345678910111213141516

LinkedHashMap<String, Integer> l = new LinkedHashMap<>();l.put("a", 11);l.put("b", 22);l.put("c", 33);l.put("d", 44);Iterator ita = l.entrySet().iterator();while (ita.hasNext()) {    Map.Entry entry = (Map.Entry) ita.next();    System.out.println(entry.getKey() + "=" + entry.getValue());}// 输出:a=11b=22c=33d=44

4. Set 集合,不可重复

常用的 Set 实现有:HashSet、LinkedHashSet、TreeSet、EnumSet。

4.1 HashSet 哈希集合

HashSet 是基于 HashMap 实现的集合,对 HashMap 做了一些封装。数据结构如图:
java基础
与 HashMap 不同的是元素的保存为链表形式,插入数据时遍历链表查看是否有相同数据,有则返回 false,没有返回 true。

1234

HashSet s = new HashSet();s.add("aa");System.out.println(s.add("aa")); // falseSystem.out.println(s.add("bb")); // true

4.2 LinkedHashSet 链表集合

继承自 HashSet 与 LinkedHashMap 相似,是对 LinkedHashMap 的封装。

4.3 TreeSet 红黑树集合

与 TreeMap 相似。是对 TreeMap 的封装。

本文只是对 Java 中的集合类做了个简单介绍,详细设计请查看源码了解详情。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
2年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这