面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,而想要存储多个对象,就不能是一个基本的变量,而应该是一个容器类的变量,StringBuffer的结果是一个字符串,对象数组的长度是固定的,为了适应变化的需求,即创造一个可变的对象的容器,Java就提供了集合类。
数组和集合的区别:
A:长度区别:数组的长度固定 集合长度可变
B:内容不同:数组存储的是同一种类型的元素 集合可以存储不同类型的元素
C:元素的数据类型:数组可以存储基本数据类型,也可以存储引用数据类型 集合只能存储引用类型
集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
Collection(单列集合)
总接口:Collection (util包下)
是集合的顶层接口,它的子体系有重复的,有唯一的,有有序的,有无序的。
功能:
添加功能
boolean add(Object obj) 添加一个元素(这个方法永远返回的是true,把object作为一个元素添加)
boolean addAll(Collection c) 添加一个集合的元素(无论重复与否,把collection中的每一个元素添加)
删除功能
void clear() 移除所有元素
boolean remove(Object o) 移除一个元素
boolean removeAll(Collection c) 移除一个集合的元素(只要有一个元素被移除了就会返回true)
判断功能
boolean contains(Object obj) 判断集合中是否包含指定的元素
boolean containsAll (Collection c) 判断集合中是否包含指定的集合元素(只有包含所有的元素才叫包含)boolean isEmpty() 判断集合是否为空
获取功能
Iteratoriterator() 迭代器,集合的专用遍历方式长度功能
int size() 元素的个数(数组和字符串的长度方法都是length方法,集合是size方法)
交集功能
boolean retainAll(Collection c) 两个集合都有的元素(假设有两个集合A,B,A对B做交集,最终的结果保存在A中,B不变,返回值表示的是A是否发生过改变)
把集合转换为数组
Object[] toArray()
集合的遍历:依次获取集合中的每一个元素
1.Object[] toArray():把集合转换为数组,通过对数组的遍历,可以实现集合的遍历 (然而这种方法object中没有length方法,所以无法得到元素的长度,所以还需要对object进行转换为字符串,这种转型是向下转型,需要强转,转完就可以调用字符串的length方法得到长度)
2.Iterator
Object next() 获取元素,并移动到下一个位置
boolean hasNext() 如果仍有元素可以迭代,则返回true
可以用while循环while(it.hasNext){},也可以用for循环for(Iterator it= c.iterator;it.hasNext();){}遍历
不要多次使用it.next()方法,因为每次使用都是调用下一个对象
迭代器:是遍历集合的一种方式。迭代器是依赖于集合而存在的。
迭代器在真正的具体的子类中,以内部类的方式实现的。
集合的使用步骤:
创建集合对象
创建元素对象
把元素添加到集合
遍历集合:
a.通过集合对象获取迭代器对象
b.通过迭代器对象的hasNext()方法判断是否有元素
c.通过迭代器对象的next()方法获取元素并移动到下一个位置
List
有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
与 set 不同,列表通常允许重复的元素。
特点:有序(存储和取出的元素一致),可重复的。
List接口的特有功能:
1.添加功能:
void add(int index,Object element) 在指定的位置添加元素(index是length长度的值就不会报错,超过这个值就会出现越界)
2.获取功能:
Object get(int index) 获取指定位置的元素
3.列表迭代器:
ListIterator listIterator() List集合特有的迭代器(该迭代器继承了Iterator迭代器,所以可以直接使用hasNext()和next()方法)
特有功能:
Object previous() 获取上一个元素
boolean hasPrevious() 判断是否有元素
ListIterator可以实现逆向遍历,但是必须先正向遍历,才能逆向遍历,否则没有输出(因为刚开始指针在第一个位置上,上一个元素没有值)无意义,所以一般不使用。
4.删除功能:
Object remove(int index) 根据索引删除元素,返回被删除的元素
5.修改功能:
Object set(int index,Object element) 根据索引修改元素,返回被修改的元素
List集合的特有遍历:size()和get()方法的结合
并发修改异常:迭代器是依赖于集合而存在的,在判断成功后,集合添加了新的元素,而迭代器不知道,所以会报错。
解决方案:
A:迭代器迭代元素,迭代器修改元素(Iterator迭代器没有添加功能,而ListIterator迭代器有add方法,但是这种方法添加的元素是在迭代的元素后面的)
B:集合遍历元素,集合修改元素(普通for循环,元素是在最后添加的)
ArrayList
底层数据结构是数组,查询快,增删慢。线程不安全,效率高。
Vector
底层数据结构是数组,查询快,增删慢。线程安全,效率低。(基本不用了,后面有替代)
特有功能:
1.添加功能:
public void addElement(Object obj)
2.获取功能:
public Object elementAt(int index)
public Enumeration elements() (相当于迭代器)有两个方法:
boolean hasMoreElements()
object nextElement()
这个麻烦,所以用现在的迭代器
JDK升级的原因:安全,效率,简化书写
LinkList
底层数据结构是链表,查询慢,增删快。线程不安全,效率高。
LinkedList类特有功能:
添加功能:
public void addFirst( Object obj) 可以实现栈的先进后出
public void addLast( Object obj)
获取功能:
public Object getFirst()
public Object getLast()删除功能:
public Object removeFirst()
public Object removeLast()
三种选用的话,一般情况下,用的是ArrayList,vector基本不用,查询多用ArrayList,增删多用LinkList。
例子:
1.ArrayList去除集合中字符串的重复值(字符串的内容相同)
A:常见集合对象
B:添加多个字符串元素(包含内容相同的)
C:创建新集合
D:遍历旧集合,获得每一个元素
E:拿这个元素到新集合去找,看有没有:有,就不管,没有就添加到新集合
F:遍历新集合
如果要求不能创建新的集合呢:可以借鉴选择排序思想,注意如果有连续的字符情况,替换后会少判断一次,所以再加个y–
2.ArrayList去除集合中自定义对象的重复值(对象的成员变量值都相同时才算重复)
如果用字符串的判断方法,会发现做不出来:因为contains()方法的底层依赖的是equals()方法,而自定义方法没有equals()方法时调用的是父类Object类的方法,该方法没有被重写,比较的是地址值,每个对象的地址值都不一样,所以无法去除重复值。
3.用LinkedList模拟栈(stack)数据结构的集合,并测试
是要自己构建一个类,底层实现使用的是LinkedList,这个类的功能要完成的是栈数据结构的功能。
集合的toString方法源码:子类中没有某一种方法的时候,去父类中查找。
Java8 使用 stream().filter()过滤List对象(查找符合条件的对象集合)
1 | List<StudentInfo> boys = studentList.stream().filter(s->s.getGender() && s.getHeight() >= 1.8).collect(Collectors.toList()); |
Set
一个不包含重复元素的 collection(即使存储了多个重复元素,打印时候仍是不重复的元素)。
无序(存储顺序和取出顺序不一致)
唯一:存储时的顺序可能和读取时的数据一致,但这代表不了有序,因为这是特例,还有可能其他情况
HashSet
它不保证set的迭代顺序;特别是不保证该顺序恒久不变。
HashSet如何保证元素唯一性:底层数据结构是哈希表(元素是链表的数组);哈希表依赖于哈希值存储
添加功能add()底层依赖两个方法:
int hashCode()
boolean equals(Object obj)
首先比较哈希值hashCode()值:
如果相同:比较地址值或者equals():true则重复不添加,false则不重复,添加到集合中
如果不同:就直接添加到集合中
如果直接写自定义案例HasSet还是会出现重复的元素,是因为你自定义的类没有重写hashCode()方法和equals()方法,所以不会进行判断,会直接添加到集合中。可以直接快捷键alt+shift+h构造hashCode()方法和equals()方法。
LinkedHashSet
元素有序唯一,底层数据结构由哈希表和链表组成
由链表保证元素有序
由哈希表保证元素唯一
TreeSet
能够对元素按照某种规则进行排序
使用元素的自然顺序对元素进行排序(自然排序,无参构造)或者根据创建 set 时提供的 Comparator 进行排序(比较器排序,带参构造),具体取决于使用的构造方法。
当实现带参构造的这个构造时,可以放在基本类中,也可以单独定义一个类实现Comaprator接口,但推荐的是直接使用内部类,在new TreeSet的时候直接在()写这个接口的实现类
特点:排序和唯一
TreeSet是如何保证元素的排序和唯一性的:底层数据结构是红黑树(红黑树是一种自平衡的二叉树)
唯一性:
是根据比较的返回值是否是0来决定的。
排序:
A.自然排序(元素具备比较性):让元素所属的类实现自然排序接口Comaprable
B.比较器排序(集合具备比较性):让集合的构造方法接收一个比较器接口的子类对象Comparator(实现的时候注意主要条件,次要条件(字面意思和隐含的意思都要实现)可以用多个三目运算符实现)
源码:看TreeMap的方法。真正的比较是依赖于元素的compareTo()方法,而这个方法是定义在Comparable里面的,所以要重写该方法必须实现Comparable接口。这个接口的默认就是自然排序
总结
一般用HashSet
PriorityQueue
Map(双列集合)
特点:将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值。可以存储键值对的元素。
Map集合存储元素是成对出现的;键是唯一的,值是可重复的。
Collection集合存储元素是单独出现的;Collection集合下Set是唯一的,List是可重复的。
注意:Map集合的数据结构值仅仅针对键有效,跟值无关;Collection集合的数据结构是针对元素有效
基本功能
添加功能:
V put(K key,V value) 添加元素;修改元素
如果键是第一次存储,就直接存储元素,并返回null
如果键不是第一次存储,就用值把以前的值替换掉,返回以前的值
删除功能:
void clear() 移除所有的键值对元素(太暴力不建议使用)
V remove(Object key) 根据键删除键值对元素,并把值返回
判断功能:
boolean containsKey(Object key) 判断集合是否包含指定的键
boolean containsValue(Object value) 判断集合是否包含指定的值 boolean isEmpty() 判断集合是否为空
获取功能:
Set<Map.Entry<K,V>> entrySet()返回的是键值对对象的集合(entry是条目的意思)
V get(Object key) 根据键获取值(如果不存在返回null)
Set
keySet() 获取集合中所有键的集合 Collection
values() 获取集合中所有值的集合 长度功能:
int size() 返回集合中的键值对的对数
Map集合的遍历:
A. 根据键找值:
(1)获取所有的键的集合
(2)遍历键的集合,获取得到每一个键值
(3)根据键去找值
B. 根据键值对对象找键和值
(1)获取所有键值对对象的集合
(2)遍历键值对对象的集合,得到每一个键值对对象
(3)根据键值对对象获取键和值
HashMap
是基于哈希表的Map接口实现
哈希表的作用是用来保证键的唯一性。
以0开头的是八进制,注意后面的数字中不能超过8。
LinkedHashMap
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。
由链表保证键的有序(有序指的是存储和取出的顺序一致)
由哈希表保证键的唯一性
TreeMap
是基于红黑树的Map接口的实现
键是红黑树结构,可以保证键的排序和唯一性
集合的嵌套遍历:
HashMap嵌套HashMap
HashMap嵌套ArrayList
ArrayList嵌套HashMap
HashMap:先创建HashMap的集合,再获取key的set集合,再遍历的时候增强for循环遍历得到key对应的value,最后再输出。
ArrayList:先创建ArrayList的集合,再遍历的时候直接增强for循环。
HashMap和Hashtable的区别(t就是小写):
HashMap:线程不安全,效率高。允许null键和null值
Hashtable:线程安全,效率低。不允许null键和null值
List,Set不是继承自Map接口,它们继承自Collection接口。Map接口本身就是一个顶层接口。
Collections类是Object包下由静态方法组成。是针对集合进行操作的工具类。
Collections成员方法:
public static
public static
public static
public static void reverse(List<?> list) 反转
public static void shuffle(List<?> list) 随机置换(每调一次方法就会对List中的元素随机排序)
如果同时有自然排序和比较器排序,以比较器排序为主
Collection和Collections的区别:
Collection:是单列集合的顶层接口,有子接口List和Set
Collections:是针对集合进行操作的工具类,有对集合进行排序和二分查找的方法。
该使用哪种集合:看需求
是否是键值对象形式:
是:Map
键是否需要排序:
是:TreeMap
否:HashMap
否:Collection
元素是否唯一:
是:Set
元素是否需要排序:
是:TreeSet
否:HashSet
不知道就用HashSet
否:List
线程要安全吗:
是:Vector(其实也不用,多线程中有个更好的方法)
否:ArrayList或者LinkedList
增删多:LinkedList
查询多:ArrayList
不知道就用ArrayList
不知道就用ArrayList
集合的常见方法及遍历方式:
Collection:
add()
remove()
contains()
iterator()
size()
遍历:增强for和迭代器
|–List
get()
遍历:多了个普通for
|–Set
Map:
put()
remove()
containsKey(),containsValue()
keySet()
get()
value()
entrySet()
size()
遍历:根据键找值;根据键值对对象分别找键和值
map.merge()
将新的值赋值到 key (如果不存在)或更新给定的key 值对应的 value
分组求和这类的操作,虽然 stream 中有相关 groupingBy() 方法,但如果你想在循环中做一些其他操作的时候,merge() 还是一个挺不错的选择的。
常规:
1 | ObjectMapper objectMapper = new ObjectMapper(); |
merge方法
1 | Map<String, Integer> studentScoreMap2 = new HashMap<>(); |