校园网自助网站,如何做自己微网站,网页商城设计,厦门市建设工程造价网有时候需要存储一组数据#xff0c;之前使用数组#xff0c;但是数组具有固定的容量#xff0c;但是在写程序时并不知道需要多少对象#xff0c;在java.util包下提供了一套完整的集合类#xff0c;包含List、Set、Queue、Map。java集合类都可以自动地调整自己的大小。 在创… 有时候需要存储一组数据之前使用数组但是数组具有固定的容量但是在写程序时并不知道需要多少对象在java.util包下提供了一套完整的集合类包含List、Set、Queue、Map。java集合类都可以自动地调整自己的大小。 在创建集合时经常使用泛型可以在编译期防止将错误的类型放入到集合中。 集合概念 集合分为两个基本接口 集合(Collection)一个独立元素的序列List必须已插入顺序保存元素Set不能包含重复元素Queue按照排队规则来确定对象产生的顺序一般是插入顺序 映射(Map)一组成对的键值对对象允许使用键来查找值。map允许我们使用一个对象来查找另一个对象 Arrays.asList()的输出是一个List但是底层实现是数组没法调整大小。 ListString list Arrays.asList(123,234);list.add(345);//java.lang.UnsupportedOperationException List 存储有序可以重复的元素相当于动态数组 集合中元素所在类要重写equals方法 ArrayList LinkedList Vector 两种类型的list ArrayList擅长随机访问元素但在List中间插入和删除元素时速度较慢 LinkedList擅长在List中间进行插入和删除操作提供了优化的顺序访问对于随机访问相对较慢 List特性 允许插入重复元素 允许插入多个null元素 List提供了ListIterator迭代器可以提供双向访问 ArrayList和Vector的异同点 相同点 两者都是基于索引的内部使用数组 两者维护插入顺序可以根据插入顺序来获取元素 ArrayList和Vector的迭代器实现都是fail-fast的 ArrayList和Vector两者都允许null值也可以使用索引值对元素进行随机访问 不同点 Vector是同步的ArrayList不是但是已过时使用CopyOnWriteArrayList ArrayList比Vector快 LinkedList链表 LinkedList添加了一些方法使其可以被用作栈队列和双向队列方法差异 getFirst()和element()是相同的都是返回列表的头部而并不删除它如果list为空则抛出NoSuchElementException异常。peek()方法在列表为空时返回null removeFirst()和remove()方法相同删除并返回列表头部元素在列表为空时返回NoSuchElementException异常poll()在列表为空时返回null addFirst()在列表头部插入一个元素 offer()和add()和addLast()相同在列表尾部添加一个元素 removeLast()删除并返回列表的最后一个元素 ArrayList和LinkedList的区别 ArrayList是由数组支持的基于索引的数据结构支持对元素的随机访问复杂度为O(1)但是LinkedList是基于链表的存储一系列的节点数据每个节点都与前一个节点和下一个节点相连。虽然存在使用索引获取元素的方法但是内部实现是从起始点开始遍历的时间复杂度是O(n) 与ArrayList相比在LinkedList中插入、添加和删除一个元素会更快 LinkedList比ArrayList消耗更多内存因为需要存储前后节点的引用 迭代器Iterators Iterator Iterator接口提供了遍历任何Collection的接口取代了java集合框架中的Enumeration迭代器允许调用者在迭代过程中移除数据 iterator只能单向移动 使用iterator()方法使集合返回一个Iterator。Iterator将准备好返回序列中的第一个元素。 使用next()方法获得序列中的下一个元素。 使用hasNext()方法检查序列中是否含有元素。 使用remove()方法将迭代器最近返回的那个元素删除。 Enumeration和iterator的区别 Enumeration的速度是Iterator的两倍使用内存也少但是iterator更加安全使得一个集合在遍历时会阻止其他线程去修改集合Iterator允许移除元素 Iterator支持fail-fast机制而Enumeration不支持Iterator遍历时当其他线程修改集合内容时迭代器会立马感知到引起快速失败抛出ConcurrentModificationException异常 Enumeration本身不支持同步只是在Vector和hashtable实现Enumeration时添加了同步 ListIterator ListIterator是Iterator的子类型只能由各种List类生成 Iterator只能向前移动ListIterator可以双向移动可以生成迭代器在列表中指向位置的后一个和前一个元素的索引。 堆栈stack 堆栈是后进先出(LIFO)最后压入(push)栈的元素第一个被弹出(pop)栈。 java1.0中有一个stack类但是设计的不好Java6添加了ArrayDeque其中包含了直接实现堆栈功能的方法 push()添加元素到栈底 peek()和pop()返回对象peek()返回栈顶元素但不从栈顶删除而pop()删除并返回栈顶元素 Set Set不保存重复的元素。查找是Set最重要的操作选择HashSet实现针对快速查找进行了优化。 存储无序不可重复 添加Set集合中的元素所在类要重写equals和hashCode方法 无序性指的是元素在底层存储的位置是无序的 HashSet没有顺序使用散列函数HashSet维护顺序与TreeSet或LinkedHashSet不同因为它们实现具有不同的元素存储方式 LinkedHashSet 也使用了散列使用了链表来维护元素的插入顺序结果将按元素的插入顺序显示。元素必须定义hashCode()和equals()方法遍历元素时会按照添加的进去的顺序 TreeSet将元素存储在红黑树数据结构可以从Set中获取有序序列其中元素必须实现Comparable接口 要求添加进TreeSet的必须是同一个类的 两种排序方式 1自然排序添加的类要实现Comparable接口重写compareTo方法 2定制排序: 使用TreeSet(Comparator? super E comparator) 构造器 重写compare(T o1, T o2);方法 Map 键值 key不可重复一个key-value组成一个entry map的分类 HashMap专为快速访问而设计TreeMap保持键始终处于排序状态没有HashMap快。LinkedHashMap按插入顺序保存其元素但使用散列提供快速访问的能力。 HashMap 基于哈希表的实现。为插入和定位键值对提供了常数时间性能。可以通过构造方法调整性能这些构造方法允许设置哈希表的容量和装填因子。可以添加key为nullvalue为null LinkedHashMap 与HashMap类似但是当遍历时可以按照插入顺序或最近最少使用(LRU)顺序获取键值对。只比HashMap略慢一个例外是在迭代时由于其使用链表维护内部顺序所以会更快些按照添加进Map的顺序遍历 TreeMap 基于红黑树实现当查看键或键值对时按排序顺序(由Comparable或Comparator确定)。TreeMap的侧重点在于按排序顺序获得结果。TreeMap是唯一使用subMap()方法的Map返回红黑树的一部分按照key所在类的指定属性进行排序要求key是同一个类的对象同TreeSet WeakHashMap 一个具有弱键的Map为了解决某些类型的问题它允许释放Map所引用的对象。如果Map外没有对特定键的引用则可以对该键进行垃圾回收 ConcurrentHashMap 不使用同步锁定的线程安全Map IdentityHashMap 使用来比较键仅用于解决特殊问题 HashTable 不可添加key为nullvalue为null的 子类Properties 处理属性文件 HashMap工作情况 HashMap在Map.Entry静态内部类实现存储键值对HashMap使用哈希算法在put和get方法中使用hashCode和equals方法使用put方法时使用key的hashcode和哈希算法来找出存储键值对的索引Entry存储在LinkedList中如果存在entry使用equals检查传递的key是否存在如果存在会覆盖掉value如果不存在会创建一个新的entry然后保存。get的时候也是先通过hashcode找到数组中的索引然后使用equals找到正确的Entry在进行取值 HashMap默认初始容量是32负载因子是0.75阈值是容量乘以负载因子当map的大小比阈值大时HashMap会对map的内容进行重新哈希。 HashMap和HashTable的区别 HashMap允许key和value为nullHashTable不允许 HashTable是同步的HashMap不是 HashMap可以转为LinkedHashMap使得遍历有序HashTable的顺序无法预知 HashMap提供对key的set进行遍历所以是fail-fast的HashTable提供对key的Enumeration进行遍历不支持fail-fast HashTable应该被CocurrentHashMap替代 队列 队列操作 队列是一个先进先出(FIFO)集合LinkedList实现了Queue接口并且提供了一些方法支持队列行为 offer()在队列尾部插入一个元素 peek()和element()返回队列头而不删除它如果队列为空element()抛出NoSuchElementException而peek()返回null poll()和remove()都删除并返回队头元素如果队列为空poll()返回nullremove()抛出NoSuchElementException PriorityQueue优先级队列 优先级队列声明下一个弹出的元素是最需要的元素。 BlockingQueue队列 是concurrent包下的类在进行检索或移除一个元素的时候会等待队列变成非空当添加一个元素的时候会等待队列中的可用空间。主要用于实现生产者-消费者模式 Collections工具类 unmodifiableCollection方法 Collections.unmodifiableCollection(list)Collections.unmodifiableList(list)使用该方法会创建一个只读集合所有改变集合的操作都会抛出UnsupportedOperationException public static T CollectionT unmodifiableCollection(Collection? extends T c) { return new UnmodifiableCollection(c);} synchronizedCollection方法 Collections.synchronizedCollection(list)方法可以创建一个线程安全的集合 public static T CollectionT synchronizedCollection(CollectionT c) { return new SynchronizedCollection(c);} 问题 1、遍历时移除List中的元素 使用forEach和Iterator 在使用forEach遍历时实际上是使用的Iterator使用的核心方法是hasNext()和next()但是使用的是list.remove来看个例子 //源码public class TestList { public static void main(String[] args) { ListString list new ArrayList(); list.add(J); list.add(A); list.add(V); list.add(A); for (String s: list) { list.remove(s); } }}//编译之后public class TestList { public TestList() { } public static void main(String[] args) { ListString list new ArrayList(); list.add(J); list.add(A); list.add(V); list.add(A); Iterator var2 list.iterator(); while(var2.hasNext()) { String s (String)var2.next(); list.remove(s); } }} 之前说过Iterator在遍历时不允许其他线程对该集合进行操作看一下ArrayList的iterator是怎么实现的 public E next() { checkForComodification(); int i cursor; if (i size) throw new NoSuchElementException(); Object[] elementData ArrayList.this.elementData; if (i elementData.length) throw new ConcurrentModificationException(); cursor i 1; return (E) elementData[lastRet i];}final void checkForComodification() { if (modCount ! expectedModCount) throw new ConcurrentModificationException();} 在每次获取下一个元素时都会比较modCount 和 expectedModCount 然后在调用的list的remove方法会导致modCount增加modCount表示被修改次数 public E remove(int index) { rangeCheck(index); modCount; E oldValue elementData(index); int numMoved size - index - 1; if (numMoved 0) System.arraycopy(elementData, index1, elementData, index, numMoved); elementData[--size] null; // clear to let GC do its work return oldValue; } 此时iterator的next方法中两个变量就不一致了就会抛出ConcurrentModificationException异常 再看一下如果使用iterator的remove方法 public void remove() { if (lastRet 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor lastRet; lastRet -1; expectedModCount modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); }} iterator在remove之后会将modCount的值赋给expectedModCount就不会出现两个变量不等的情况了 不使用forEach遍历 使用普通for循环有两种方式第一种是使用正序遍历但是进行remove操作之后要把遍历的索引进行修正减一否则在移除下一个的时候就会出错第二种就是使用倒序遍历 // 正序遍历for (int i 0; i list.size(); i) { String s list.remove(i); i i - 1; System.out.println(s);}//倒序遍历for (int i list.size() - 1; i 0; i--) { String s list.remove(i); System.out.println(s);} 2、fail-fast和fail-safe java.util包中集合类被设计为fail-fast的而java.util.concurrent中集合为fail-safe的。fail-fast迭代器抛出ConcurrentModificationException而fail-safe迭代器从不抛出ConcurrentModificationExceptionIterator的安全失败是基于对底层集合做拷贝不受源集合上修改的影响 fail-fast fail-fast迭代器抛出ConcurrentModificationException通过modCount来进行实现在进行迭代时每次对于元素的修改都会修改该值一旦该值被修改了就会抛出异常 // 当Itr被实例化的时候记录一下迭代器被实例化时ArrayList的修改次数(在用ArrayList进行add/remove操作时modCount每次都加一)int expectedModCount modCount;// 检查是否被修改了 final void checkForComodification() { // 当修改次数与Itr被实例化时的修改次数不一致时说明在进行迭代操作的时候其他线程进行了ArrrayList的add/remove操作此时抛出ConcurrentModificationException即为fast-fail快速失败机制 if (modCount ! expectedModCount) throw new ConcurrentModificationException(); } 3、Arrays.asList 这个方法返回的是一个ArrayList不过这个ArrayList是Arrays类的内部类在调用add方法的时候会直接报错 UnsupportedOperationException这是运行时异常 public void add(int index, E element) { throw new UnsupportedOperationException();} https://zhhll.icu/2020/java基础/集合/1.java基础之集合/ 本文由 mdnice 多平台发布