本文主要是介绍Java基础回顾系列-第六天-Java集合,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Java基础回顾系列-第六天-Java集合
- 集合概述
- 数组的弊端
- 集合框架的优点
- Java集合关系图
- 集合框架体系图
- java.util.Collection接口
- List集合
- java.util.List接口
- java.util.ArrayList
- java.util.LinkedList
- java.util.Vector
- Set集合
- java.util.Set接口
- java.util.HashSet
- java.util.LinkedHashSet
- java.util.TreeSet
- 集合输出
- Iterator迭代输出
- foreach
- ListIterator双向迭代接口
- Enumeration
- Map集合
- java.util.HashMap
- java.util.LinkedHashMap
- java.util.HashTable
- java.util.TreeMap
- java.util.Map.Entry接口
- 集合工具类
- Stack栈
- Queue队列
- java.util.Properties属性操作
- java.util.Collections工具类
集合概述
参考博文:Java集合类: Set、List、Map、Queue使用场景梳理
参考官方文档:
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/doc-files/coll-reference.html
Java 集合就像一种容器,可以把多个对象的引用放入容器中。
Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组。
集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。所有的集合类都位于java.util包下,后来为了处理多线程环境下的并发安全问题,java5还在java.util.concurrent包下提供了一些多线程支持的集合类。
Java 集合可分为 Set
、List
和 Map
三种体系:
Set
:无序、不可重复的集合List
:有序,可重复的集合Map
:具有映射关系的集合,每个元素都包含key和value,key不允许重复,value可以
注意: 在 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object 类型处理;从 Java5 增加了泛型以后,Java 集合可以记住容器中对象的数据类型。
数组的弊端
既然数组可以存储多个数据,为什么还需要集合类?
- 数组长度是不可变的,一旦初始化之后,长度是固定的;
- 参考以下集合框架的优点,相对应的就是数组的缺点。
集合框架的优点
- 通过提供数据结构和算法减少编程工作,因此您不必自己编写它们。
- 通过提供数据结构和算法的高性能实现来提高性能。由于每个接口的各种实现是可互换的,因此可以通过切换实现来调整程序。
- 通过建立共同语言来来回传递集合,提供不相关API之间的互操作性。
- 通过要求您学习多个集合API 来减少学习API所需的工作量。
- 通过不要求您生成临时集合API,减少设计和实现API所需的工作量。
- 通过为集合和算法提供标准接口来促进软件重用,以便对其进行操作。
Java集合关系图
下图由于CSDN对图像尺寸有限制,可能有点模糊,可以点此访问 https://i-blog.csdnimg.cn/blog_migrate/3076e3a40b56f4cd5975f08a7fa08459.png。
集合框架体系图
java.util.Collection接口
Collection接口是单值集合操作的最大父接口,在该接口中定义有所有单值数据的处理对象。
很少直接使用此接口操作。
方法 | 语义 |
---|---|
boolean add(E e) | 添加单个实例元素到此集合中 |
boolean addAll(Collection<? extends E> c) | 将指定集合中的所有元素添加到此集合中 |
void clear() | 删除所有元素 |
boolean contains(Object o) | 是否存在指定元素的单个实例(如果存在) |
boolean remove(Object o) | 移除指定元素的单个实例(如果存在) |
int size() | 返回此集合中的元素数。 |
Object[] toArray() | 将元素转换为数组返回 |
boolean retainAll(Collection<?> c) | 是否保留交集元素 |
Iterator<E> iterator() | 返回此集合中元素的迭代器。 |
List集合
java.util.List接口
有序集合:public interface List<E> extends Collection<E>
方法 | 语义 |
---|---|
void add(int index, E element) | 在指定索引位置上增加单个实例元素 |
E set(int index, E element) | 在指定索引位置上修改单个实例元素 |
ListIterator<E> listIterator() | 返回此集合中元素的列表迭代器。 |
@SafeVarargs static <E> List<E> of(E... elements) | 返回包含任意数量元素的不可修改列表。 |
三个常用子类:
- java.util.ArrayList:大部分首选(使用比率90%),线程不安全。
- java.util.LinkedList:8%
- java.util.Vector:2%
①、List list1 = new ArrayList();
底层数据结构是数组,查询快,增删慢;线程不安全,效率高
②、List list2 = new Vector();
底层数据结构是数组,查询快,增删慢;线程安全,效率低,几乎已经淘汰了这个集合
③、List list3 = new LinkedList();
底层数据结构是链表,查询慢,增删快;线程不安全,效率高
怎么记呢?我们可以想象:
数组就像身上编了号站成一排的人,要找第10个人很容易,根据人身上的编号很快就能找到。但插入、删除慢,要望某个位置插入或删除一个人时,后面的人身上的编号都要变。当然,加入或删除的人始终末尾的也快。
链表就像手牵着手站成一圈的人,要找第10个人不容易,必须从第一个人一个个数过去。但插入、删除快。插入时只要解开两个人的手,并重新牵上新加进来的人的手就可以。删除一样的道理。
java.util.ArrayList
public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, Serializable
ArrayList存储容量不足时,会以成倍的形式开辟存储容量,比如默认的10,20,40…
构造函数 | 语义 |
---|---|
public ArrayList() | 构造一个初始容量为10的空列表。 |
ArrayList(int initialCapacity) | 构造一个指定初始容量的列表。 |
java.util.LinkedList
基于链表实现
public class LinkedList extends AbstractSequentialList implements List, Deque, Cloneable, Serializable
没有提供像ArrayList(int initialCapacity)
构造一个指定初始容量的列表。为啥?基于链表,不需要。
类型不限制,可存储为null,且元素是在最后一个元素增加。性能快。
ArrayList与LinkedList区别?
- ArrayList是数组实现的集合操作,而LinkedList是根据双向链表实现的集合操作;
- 在使用List集合中的get()方法根据索引获取数据时,ArrayList的时间复杂度为S(0),而LinkedList的时间复杂度为S(N),N为集合的长度。简而言之,ArrayList与LinkedList 相比,查询和修改值比较快,但是增删速度比较快。
- ArrayList在使用默认初始化对象数组的大小长度是10,如果空间不足会采用2倍的形式进行容量的扩充,如果保存大数据量的时候有可能会造成垃圾的产生以及性能的下降,但是这个时候可以使用LinkedList的子类保存。
java.util.Vector
public class Vector extends AbstractList implements List, RandomAccess, Cloneable, Serializable
是一个原始古老的程序类。 Vector和ArrayList一样,都是通过数组实现的,但是Vector是线程安全的。和ArrayList相比,其中的很多方法都通过同步(synchronized)处理来保证线程安全。
如果你的程序不涉及到线程安全问题,那么使用ArrayList是更好的选择(因为Vector使用synchronized,必然会影响效率)。
二者之间还有一个区别,就是扩容策略不一样。在List被第一次创建的时候,会有一个初始大小,随着不断向List中增加元素,当List认为容量不够的时候就会进行扩容。Vector缺省情况下自动增长原来一倍的数组长度,ArrayList增长原来的50%。
Set集合
java.util.Set接口
Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false。
Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回true,Set就不 会接受这两个对象。
有序集合:public interface Set<E> extends Collection<E>
注意:是Set集合并不像List集合扩充了许多新的方法,所以无法使用List获取指定元素的方法
public E get(int index)
。
二个常用子类:
- java.util.HashSet:
- java.util.LinkedHashSet:
- java.util.TreeSet:
java.util.HashSet
HashSet有以下特点
- 不能保证元素的排列顺序,顺序有可能发生变化
- 不是同步的
- 集合元素可以是null,但只能放入一个null
当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。
简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相 等
注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对 象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。
java.util.LinkedHashSet
LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。
java.util.TreeSet
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0
自然排序
自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。
obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。
如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0
定制排序
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法
public class TreeSet extends AbstractSet implements NavigableSet, Cloneable, Serializable
自定义类排序时,自定义类必须实现Comparable接口,实现方法
如果自定义类属性有很多呢?这种情况下首选HashSet实现,使用hashCode(对象编码)以及equals(对象比较)来比较。
集合输出
Iterator迭代输出
该方式适用于Collection的所有子类。 使用频率比重高。
方法 | 语义 |
---|---|
boolean hasNext() | 判断迭代中是否有下一个元素。 |
E next() | 返回迭代中的下一个元素。 |
default void remove() | 从底层集合中移除此迭代器返回的最后一个元素。 |
Java 中的 Iterator 功能比较简单,并且只能单向移动:
- 使用方法 iterator() 要求容器返回一个 Iterator。第一次调用 Iterator 的 next() 方法时,它返回序列的第一个元素。注意:iterator() 方法是 java.lang.Iterable 接口,被 Collection 继承。
- 使用 next() 获得序列中的下一个元素。
- 使用 hasNext() 检查序列中是否还有元素。
- 使用 remove() 将迭代器新返回的元素删除。
foreach
该方式适用于Collection的所有子类。 使用频率比重一般。
ListIterator双向迭代接口
仅适用于List接口的子类。使用频率比重极低。
Iterator输出特点:只能够由前向后进行内容的迭代处理,而如果要想进行双向迭代,那么就必须依靠Iterator的子接口:ListIterator来实现。
hasPrevious():判断是否有上一个元素
previous():取得上一个元素
listIterator():取得ListIterator接口对象
如果要想由后向前遍历,只能先执行由前向后遍历。
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("HaHa");
list.add("HeHe");
ListIterator<String> listIterator = list.listIterator();
System.out.println("从后往前输出");
while (listIterator.hasNext()) {//判断是否有下一个元素System.out.println(listIterator.next()+"、");//取得下一个元素
}
System.out.println("\r\n从后往前输出");
while (listIterator.hasPrevious()) {//判断是否有上一个元素System.out.println(listIterator.previous()+"、");//取得上一个元素
}
Enumeration
仅适用于Vector类。
hasMoreElements():判断是否有下一个元素
nextElement():取得下一个元素
elements():取得Enumeration的接口对象
Map集合
特点:
- Map用于保存具有"映射关系"的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另外一组值用于保存Map里的value。
- key和value都可以是任何引用类型的数据。
- Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较结果总是返回false。
关于Map,我们要从代码复用的角度去理解,java是先实现了Map,然后通过包装了一个所有value都为null的Map就实现了Set集合。
Map的这些实现类和子接口中key集的存储形式和Set集合完全相同(即key不能重复)
Map的这些实现类和子接口中value集的存储形式和List非常类似(即value可以重复、根据索引来查找)
1.HashMap和Hashtable的效率大致相同,因为它们的实现机制几乎完全一样。但HashMap通常比Hashtable要快一点,因为Hashtable是线程同步的,不过该类(Hashtable)是个古老类,建议使用ConcurrentHashMap代替 Hashtable。
2. TreeMap通常比HashMap、Hashtable要慢(尤其是在插入、删除key-value对时更慢),因为TreeMap底层采用红黑树来管理key-value对。
3. 使用TreeMap的一个好处就是: TreeMap中的key-value对总是处于有序状态,无须专门进行排序操作。
方法 | 语义 |
---|---|
V put(K key, V value) | 将指定的值与此映射中的指定键相关联 |
V get(Object key) | 返回指定键映射到的值,或者null此映射是否不包含键的映射。 |
Set<Map.Entry<K,V>> entrySet() | 返回Set此映射中包含的映射的视图。 |
boolean containsKey(Object key) | 是否存在指定key。 |
boolean containsValue(Object value) | 是否存在指定value |
Set<K> keySet() | 集合的key转为Set。 |
V remove(Object key) | 移除指定key项的值 |
java.util.HashMap
和HashSet集合不能保证元素的顺序一样,HashMap也不能保证key-value对的顺序。并且类似于HashSet判断两个key是否相等的标准也是: 两个key通过equals()方法比较返回true、同时两个key的hashCode值也必须相等。
任何null对象都可以用作键或值,不论key或value都可以为null。
put的使用
put(“A”, 1);//key不重复返回null。
put(“A”, 2);//key重复返回1旧值。
初始化容量为16个,容量扩充是(当前容量*0.75),初始化即12时进行成倍扩充容量数。
java.util.LinkedHashMap
LinkedHashMap也使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,与key-value对的插入顺序一致(注意和TreeMap对所有的key-value进行排序进行区
分)
java.util.HashTable
是一个古老的Map实现类。Hashtable是同步的。如果不需要线程安全的实现,建议使用它来 HashMap代替Hashtable。如果需要线程安全的高度并发实现,则建议使用ConcurrentHashMap代替 Hashtable。
任何非null对象都可以用作键或值,即不允许null对象的数据,不论key或value。
java.util.TreeMap
TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对(节点)时,需要根据key对节点进行排序。TreeMap可以保证所有的key-value对处于有序状态。同样,TreeMap也有两种排序方式: 自然排序、定制排序
key不允许为null,因为需要依赖于接口对象中的compareTo(或compare)方法执行比较
java.util.Map.Entry接口
集合工具类
Stack栈
Stack是Vector提供的一个子类,用于模拟"栈"这种数据结构(LIFO后进先出)
public class Stack extends Vector
Deques也可以用作LIFO(后进先出)堆栈。应优先使用此接口,而不是遗留Stack类。当deque用作堆栈时,元素将从双端队列的开头推出并弹出。
方法 | 语义 |
---|---|
public boolean empty() | 测试此堆栈是否为空。 |
public E peek() | 查看此堆栈顶部的对象,而不将其从堆栈中删除。 |
public E pop() | 移除此堆栈顶部的对象,并将该对象作为此函数的值返回。 |
public E push(E item) | 将项目推到此堆栈的顶部。 |
public int search(Object o) | 返回对象在此堆栈上的从1开始的位置。 |
Queue队列
Queue用于模拟"队列"这种数据结构(先进先出FIFO)。队列的头部保存着队列中存放时间最长的元素,队列的尾部保存着队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,访问元素(poll)操作会返回队列头部的元素,队列不允许随机访问队列中的元素。结合生活中常见的排队就会很好理解这个概念。
注意:LinkedList也实现了Queue。
优先级队列:PriorityQueue 优先级队列的元素根据其 自然顺序排序,或者根据使用的Comparator 构造函数在队列构造时提供。请注意,此实现不同步。PriorityQueue 如果任何线程修改队列,则 多个线程不应同时访问实例。相反,使用线程安全PriorityBlockingQueue类。
java队列——queue详细分析:https://www.cnblogs.com/lemon-flm/p/7877898.html
Deque接口及其实现提供了更完整和一致的LIFO堆栈操作集,应该优先使用该类。例如:
Deque stack = new ArrayDeque();
java.util.Properties属性操作
Properties 继承于 Hashtable.表示一个持久的属性集.属性列表中每个键及其对应值都是一个字符串,且该类是线程安全的。
Properties 类被许多Java类使用。例如,在获取环境变量时它就作为System.getProperties()方法的返回值。主要用于读取配置信息。
类结构:
public class Properties extends Hashtable<Object,Object>
注意:因为Properties继承自Hashtable,所以 put和putAll方法可以应用于 Properties对象。强烈建议不要使用它们,因为它们允许调用者插入其键或值不是的条目 Strings。
方法 | 语义 |
---|---|
public void load(InputStream inStream) throws IOException | 从输入字节流中读取属性列表(键和元素对)。 |
public void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException | 将指定输入流上的XML文档表示的所有属性加载到此属性表中。 |
public void storeToXML(OutputStream os, String comment) throws IOException | 发出表示此表中包含的所有属性的XML文档。 |
public String getProperty(String key, String defaultValue) | 搜索具有指定键的属性。 |
public Object setProperty(String key, String value) | 设置指定键的属性,调用Hashtable方法put。 |
Java关于Properties用法的总结(一):https://www.cnblogs.com/bakari/p/3562244.html
java读取properties文件总结:http://www.cnblogs.com/xdp-gacl/p/3640211.html
java.util.Collections工具类
Java中Collection和Collections的区别 https://www.cnblogs.com/cathyqq/p/5279859.html
Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
public class CollectionsTest {public static void main(String[] args) {List<Integer> list = new ArrayList<Integer>();list.add(34);list.add(55);list.add(56);list.add(89);list.add(12);list.add(23);list.add(126);System.out.println(list);//对集合进行排序Collections.sort(list);System.out.println(list);//对集合进行随机排序Collections.shuffle(list);System.out.println(list);//获取集合最大值、最小值int max = Collections.max(list);int min = Collections.min(list);System.out.println("Max:" + max + " Min: " + min);List<String> list2 = Arrays.asList("Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday".split(","));System.out.println(list2);//查找集合指定元素,返回元素所在索引//若元素不存在,n表示该元素最有可能存在的位置索引int index1 = Collections.binarySearch(list2, "Thursday");int index2 = Collections.binarySearch(list2, "TTTTTT");System.out.println(index1);int n = -index2 - 1;//查找子串在集合中首次出现的位置List<String> subList = Arrays.asList("Friday,Saturday".split(","));int index3 = Collections.indexOfSubList(list2, subList);System.out.println(index3);int index4 = Collections.lastIndexOfSubList(list2, subList);System.out.println(index4);//替换集合中指定的元素,若元素存在返回true,否则返回falseboolean flag = Collections.replaceAll(list2, "Sunday", "tttttt");System.out.println(flag);System.out.println(list2);//反转集合中的元素的顺序Collections.reverse(list2);System.out.println(list2);//集合中的元素向后移动k位置,后面的元素出现在集合开始的位置Collections.rotate(list2, 3);System.out.println(list2);//将集合list3中的元素复制到list2中,并覆盖相应索引位置的元素List<String> list3 = Arrays.asList("copy1,copy2,copy3".split(","));Collections.copy(list2, list3);System.out.println(list2);//交换集合中指定元素的位置Collections.swap(list2, 0, 3);System.out.println(list2);//替换集合中的所有元素,用对象objectCollections.fill(list2, "替换");System.out.println(list2);//生成一个指定大小与内容的集合List<String> list4 = Collections.nCopies(5, "哈哈");System.out.println(list4);//为集合生成一个EnumerationList<String> list5 = Arrays.asList("I love my country!".split(" "));System.out.println(list5);Enumeration<String> e = Collections.enumeration(list5);while (e.hasMoreElements()) {System.out.println(e.nextElement());}}
}
方法 | 语义 |
---|---|
Collections.sort(list) //list: 1 2 4 5 6 7 8 | list升序。 |
Collections.sort(list,Collections.reverseOrder())//list:8 7 6 5 4 2 1 | list降序。 |
Collections.reverse(list)//list:4 1 8 6 2 7 5 | list逆序。 |
Arrays.sort(a)//a: 1 2 4 5 6 7 8 | 数组升序。 |
Arrays.sort(a,Collections.reverseOrder())//a: 8 7 6 5 4 2 1 | 数组降序。 |
这篇关于Java基础回顾系列-第六天-Java集合的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!