集合
1)缘由
为了方便对对象进行操作
2)数组和集合的区别?
都可以存储对象
数组的长度不可变,集合的长度可变
数组还可以存储基本数据类型,集合不可以
3)特点
专门用来存储对象:长度可变,可以存放不同类型的对象
1、Collection
是集合的根接口。集合里面元素可以是有序的,可以是无序的,可以是重复的,可以是唯一的
1)添加元素
add(Object obj):添加元素
addAll(Collection c):添加集合元素
2)删除功能
clear():清空
remove(Object obj):删除元素
removeAll(Collection c):删除集合元素
3)判断功能
isEmpty():是否为空
contains(Object obj):是否包含指定元素
containsAll(Collection c):是否包含指定的集合元素
4)获取功能
size():集合大小
Iterator<E> iterator():获取迭代器对象
5)、其他功能
Object[] toArray():集合转数组
retainAll(Collection c):两个集合交集操作
2、List接口
继承Collection接口,元素有序可重复
成员方法
add(int index,E ele):在指定的索引处添加指定元素
remove(int index):删除指定索引处的元素
get(int index):获取指定索引处的元素
set(int index,E ele):在指定索引处设置指定元素
ListIterator listIterator():获取列表迭代器
3、ListIterator
继承Iterator,允许正向或逆向遍历,且允许在迭代期间添加集合元素
boolean hasPrevious():是否有上一个元素
Object previous():获取上一个元素
add(E ele):添加元素
4、ArrayList
该类也是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。ArrayList 增长当前长度的50%,插入删除效率低。
底层结构是数组,查询元素快,增加删除慢
线程不安全,不同步,效率高
5、Vector
底层结构是数组,查询元素快,增加删除慢
线程安全,同步,效率低
特有功能
addElement(E e):添加元素
E elementAt(int index):获取指定索引处的元素
Enumeration elements():获取集合所有元素构成的一个枚举对象
Enumeration类里面方法
hasMoreElements():是否有更多元素
nextElement():获取下个元素
6、栈Stack
栈是Vector的一个子类,它实现了一个标准的后进先出的栈。
成员方法
E peek():获取栈顶元素
E pop():弹栈(获取栈顶元素并移除)
E push(E e):压栈(将元素添加到栈中)
7、常见的数据结构
数据结构:数据存储方法
1)栈:先进后出
2)队列:先进先出
3)数组:查询快,增删慢
4)链表:查询慢,增删快
5)树
6)哈希表
8、LinkedList(双向链表)
该类实现了List接口,允许有null(空)元素。主要用于创建链表数据结构,该类没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步,解决方法就是在创建List时候构造一个同步的List。例如:
List list = Collections.synchronizedList(new LinkedList(...));
底层是链表,查询慢,增删快
线程不安全,效率高
特有功能
addFirst(E e):添加首元素
addLast(E e):添加末元素
getFirst():获取首元素
getLast():获取末元素
E removeFirst():移除首元素
E removeLast():移除末元素
集合的遍历方式:
public static void main(String[] args) {
List list = new ArrayList();
//添加指定元素
list.add("hello");
list.add("java");
list.add("world");
//在Java后面添加javase
list.add(2,"javase");
System.out.println(list);
//删除javase
list.remove(2);
//获取第一个元素
System.out.println(list.get(0));
//设置最后一个元素haha
list.set(list.size()-1,"haha");
System.out.println(list);
//遍历方式1
/*Iterator i = list.iterator();
while(i.hasNext()){
String s = (String) i.next();
System.out.println(s);
}*/
/*//遍历方式2
for (int i = 0; i < list.size(); i++) {
String s = (String) list.get(i);
System.out.println(s);
}*/
//遍历方式3
ListIterator li = list.listIterator();
while(li.hasNext()){
String s = (String) li.next();
System.out.println(s);
}
//遍历方式4
//逆向遍历(前提,需先进行正向遍历)
// ListIterator li = list.listIterator();
while(li.hasPrevious()){
String s = (String) li.previous();
System.out.println(s);
}
题目:
写一个类MyLinked,实现双向循环链表
Node head(链表头)
size(元素个数)
Boolean add(E e):
boolean remove(E e):
int Size():
Node (节点,内部类)
element(元素)
next(下一个节点)
prev(上一个节点)
//自定义双向循环链表
class MyLinked<E>{
//链表头
private Node head;
//链表中的元素个数
private int size;
//添加元素
public boolean add(E e){
Node node = new Node();//创建节点
node.element = e;//给节点添加元素
//判断是否是首节点
if(head == null){
head = node;
head.prev = head;
head.next = head;
}else{
head.prev.next = node;
node.next = head;
node.prev = head.prev;
head.prev = node;
}
size++;
return true;
}
//移除元素
public boolean remove(E e){
//判断链表是否为空
if(size == 0){
return false;
}
Node node = head;
do{
//判断当前节点内是否存在需要删除的内容
if(node.element.equals(e)){
node.prev.next = node.next;
node.next.prev = node.prev;
size--;
if(size == 0){
head = null;
}
return true;
}else{
node = node.next;
}
}while(node != head);
return false;
}
//获取集合大小
public int size(){
return size;
}
//是否包含指定元素
public boolean contains(E e){
Node node = head;
do{
if(node.element.equals(e)){
return true;
}
node = node.next;
}while(node != head);
return false;
}
//toString
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("[");
Node node = head;
do{
sb.append(node.element).append(",");
node = node.next;
}while(node != head);
int index = sb.lastIndexOf(",");
sb.replace(index, index+1, "]");
return sb.toString();
}
class Node{
//当前节点内的元素
private E element;
//上一个节点
private Node prev;
//下一个节点
private Node next;
}
}
public class MyLinkedDemo {
public static void main(String[] args) {
MyLinked<String> ml = new MyLinked<String>();
ml.add("hello");
ml.add("world");
ml.add("java");
ml.add("world");
System.out.println(ml);
System.out.println(ml.size());
System.out.println(ml.remove("java"));
System.out.println(ml.size());
System.out.println(ml);
}
}
================输出结果=============
[hello,world,java,world]
4
true
3
[hello,world,world]
9、队列Queue(接口)
队列需要满足先进先出的原则
队列的遍历是一次性的,想要获取队列里面某个元素,需要先将队列中该元素之前的所有元素获取到,才能访问该元素
成员方法
boolean offer(E e):向队列末尾添加元素(入队)
E poll():获取并删除队列的首元素(出队)
E peek():获取队列的首元素
10、双端队列Deque
队列两端都可以进出
当使用双端队列如果只从一侧操作,就形成了一种存储模式(先进后出)
从一侧操作,入队出队方法
boolean push(E e):入队
E pop():出队
注:pop()与poll()区别?
poll是队列数据结构实现类的方法,从队首获取元素,同时获取的这个元素将从原队列删除,当队列中没有元素时,调用该方法返回hull;pop是栈结构的实现类的方法,表示返回栈顶的元素,同时该元素从栈中删除,当栈中没有元素时,调用该方法会发生异常。
11、Set接口
继承Collection,元素无序唯一的
12、SortedSet接口
继承于Set保存有序的集合。
13、TreeSet
实现的是SortedSet接口,可以实现排序等功能,底层是红黑二叉树,不能添加值为null元素,元素唯一
排序方式:
1)自然排序(元素具有比较性)
元素对应的类需要实现 Comparable接口
2)比较器排序(集合具有比较性)
构造方法接收1个比较器Comparator
TreeSet(Comparator c)
import java.util.TreeSet;
class Pig implements Comparable<Pig>{
String name;
int age;
public Pig() {
super();
}
public Pig(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Pig other = (Pig) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Pig [age=" + age + ", name=" + name + "]";
}
@Override
public int compareTo(Pig p) {
//先按年龄的由小到大进行排序
int num = this.age - p.age;
//如果年龄相同,则按姓名的的字典顺序排序
int num2 = num==0?this.name.compareTo(p.name):num;
return num2;
}
}
public class TreeSetDemo {
public static void main(String[] args) {
/*TreeSet<Integer> ts = new TreeSet<Integer>();
ts.add(30);
ts.add(50);
ts.add(10);
ts.add(20);
ts.add(40);
ts.add(30);
for (Integer i : ts) {
System.out.println(i);
}*/
/*
10
20
30
40
50
*/
Pig p1 = new Pig("xiaohei",2);
Pig p2 = new Pig("xiaobai",1);
Pig p3 = new Pig("dazhuang",4);
Pig p4 = new Pig("dabai",5);
Pig p5 = new Pig("dahei",3);
Pig p6 = new Pig("xiaojie",3);
TreeSet<Pig> ts = new TreeSet<Pig>();
ts.add(p1);
ts.add(p2);
ts.add(p3);
ts.add(p4);
ts.add(p5);
ts.add(p6);
for (Pig pig : ts) {
System.out.println(pig);
}
/++++++
Pig [age=1, name=xiaobai]
Pig [age=2, name=xiaohei]
Pig [age=3, name=dahei]
Pig [age=3, name=xiaojie]
Pig [age=4, name=dazhuang]
Pig [age=5, name=dabai]
*******/
}
}
import java.util.Comparator;
import java.util.TreeSet;
//比较器
class Chicken{
String name;
int age;
public Chicken() {
super();
}
public Chicken(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Chicken [age=" + age + ", name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Chicken other = (Chicken) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
public class TreeSetDemo02 {
public static void main(String[] args) {
Chicken c = new Chicken("xiaohuang",2);
Chicken c2 = new Chicken("xiaohua",5);
Chicken c3 = new Chicken("aohuag",3);
Chicken c4 = new Chicken("xiaoang",4);
Chicken c5 = new Chicken("aohuang",1);
Chicken c6 = new Chicken("xiang",3);
TreeSet<Chicken> ts = new TreeSet<Chicken>(new Comparator<Chicken>() {
@Override
public int compare(Chicken c1, Chicken c2) {
//先按年龄由大到小排序
int num = c2.age - c1.age;
//如果年龄相同,再按字典顺序比较姓名
int num2 = num==0?c1.name.compareTo(c2.name):num;
return num2;
}
});
ts.add(c);
ts.add(c2);
ts.add(c3);
ts.add(c4);
ts.add(c5);
ts.add(c6);
for (Chicken chicken : ts) {
System.out.println(chicken);
}
}
}
============输出结果==================
Chicken [age=5, name=xiaohua]
Chicken [age=4, name=xiaoang]
Chicken [age=3, name=aohuag]
Chicken [age=3, name=xiang]
Chicken [age=2, name=xiaohuang]
Chicken [age=1, name=aohuang]
14、HashSet类
实现Set接口,元素是无序且唯一的,允许最多出现一个值为null的元素。
底层是哈希表(元素是链表的数组,结合了数组和链表的优点)
(面试题)如何保证唯一性?
两个对象hash不同,这两个元素一定不等;反之,不能确定。
拿添加元素和同一分区内的所有元素进行比较,判断该分区内是否已经添加了该元素
比较规则:(分步骤)
①先比较两个对象的hash是否相同:如果新添加的元素和分区内所有元素的hash都不相同,那么添加该元素;
②如果新添加的元素和分区内某个元素的hash相同,这时候就看它们的地址值是否相同:
③如果不同,还要通过equals比较这两个对象是否相同:如果相等,不能添加;如果不等,添加新元素
总结为一句话:
只有所有分区内元素和新元素的地址值不同且通过equals比较,不等,添加新元素
两个对象hash不同,这两个对象一定不等,反之,不能确定。拿添加元素和同一个分区内的所有元素进行比较,判断该分区内
for(Entry<K,V> e = table[i]; e != null; e = e.next){//获取分区内所有元素
if (e.hash == hash && ((k = e.key) == key || key.equals(k))){
//之前的集合中已经存在将要添加的元素
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
//添加元素
modCount++;
addEntry(hash, key, value, i);
return null;
阐述Hash的过程
1)获取两个对象的hash
hash = e.hashCode();
hash = hash^hash>>>16;
2)定位桶(元素无序原因)
i = hash&(length-1)
3)判断桶元素
3.1桶 = null
添加链表的头元素
3.2桶 != null
添加链表的其他元素
15、LinkedHashSet
继承HashSet类,元素是有序唯一的,只允许出现一个null值
有序是由链表保证,唯一是由哈希表保证
Set和List的区别
- 1. Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
- 2. Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。
- 3. List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector> 。
16、Map(接口)
存储的是键值对、将键映射映射到值
键不可以重复,值可以重复,每一键只能映射到一个值 ,两个键可以映射两个相同的值
成员方法(口诀“1 2 3 5”)
添加功能:
V put(k v):添加键值对
删除功能:
void clear():清空
V remove(k):通过键删除对象的键值对
判断功能:
containsKey(k):是否包含指定的键
containsValue(v):是否包含指定的值
isEmpty():是否是空
获取功能:
size():键值对个数
v get (k):通过键获取值
Set<k> keySet():获取所有键组成的集合 【有序】
collection<v> values():获取所有值组成的集合【无序,用的是XX父类】
Set<Map.Entry<K.V>> entrySet():获取键值对组成的集合
17、Map.Entry
描述在一个Map中的一个元素(键/值对)。是一个Map的内部类。
18、HashMap
实现Map接口,允许出现一个null键和多个null值
HashMap和Hashtable的区别【面试题】
HashMap:线程不安全,不同步,效率高:允许出现null键和null值
Hashtable:线程安全,同步,效率低:不允许出现null键和null值
19、Dictionary
Dictionary 类是一个抽象类,用来存储键/值对,作用和Map类相似。
20、Hashtable
Hashtable 是 Dictionary(字典) 类的子类,同时实现了Map接口,位于 java.util 包中。
21、Properties
Properties 继承于 Hashtable,表示一个持久的属性集,属性列表中每个键及其对应值都是一个字符串。
22、LinkedHashMap(HashMap、【无序】)
继承HashMap,键值对是有序的
23、SortedMap(接口)
继承于 Map,使 Key 保持在升序排列。
24、TreeMap
实现了SortedMap接口,元素可以根据键进行排序,元素具有唯一性
排序方式
1)自然排序(元素具有比较性)
元素对应实现的类要实现Comparable接口
2)比较器排序(集合具有比较性)
构造方法接受1个比较器Comparator
TreeSet(Comparator c)
遍历 Map
import java.util.*;
public class Test{
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
//第一种:普遍使用,二次取值
System.out.println("通过Map.keySet遍历key和value:");
for (String key : map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
//第二种
System.out.println("通过Map.entrySet使用iterator遍历key和value:");
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第三种:推荐,尤其是容量大时
System.out.println("通过Map.entrySet遍历key和value");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第四种
System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
for (String v : map.values()) {
System.out.println("value= " + v);
}
}
}
25、Collections
针对集合进行操作的工具类
常见方法:
sort(List<T> list):集合排序,默认自然排序
sort(List<T> list,Comparator<T> c):根据比较器进行排序
binarySearch(List<T> list,T t):二分法查找
reverse(Lisr<T> list):反转
shuffle(Lisr<T> list):随机置换
max(Collection c):获取最大值
案例:斗地主(一副牌实现洗牌、发牌、给玩家手中的牌进行排序)
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.TreeSet;
//三人斗地主,洗牌发牌
public class PokerDemo {
public static void main(String[] args) {
//牌盒
HashMap<Integer,String> pokers = new HashMap<Integer,String>();
//牌号
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 54; i++) {
list.add(i);
}
//花色
String[] colors = {"黑桃","红桃","梅花","方片"};
//点数
String[] nums = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
String poker = null;
int k=0;
for (String num : nums) {
for (String color : colors) {
poker = color+num;//牌
pokers.put(k, poker);
k++;
}
}
//放入小大王
pokers.put(52,"小王");
pokers.put(53,"大王");
//洗牌
Collections.shuffle(list);
//玩家和底牌的牌盒
/*List<Integer> player01 = new ArrayList<Integer>();
List<Integer> player02 = new ArrayList<Integer>();
List<Integer> player03 = new ArrayList<Integer>();
List<Integer> diPai = new ArrayList<Integer>(); */
TreeSet<Integer> player01 = new TreeSet<Integer>();
TreeSet<Integer> player02 = new TreeSet<Integer>();
TreeSet<Integer> player03 = new TreeSet<Integer>();
TreeSet<Integer> diPai = new TreeSet<Integer>();
int i = 0;
for (int pai : list) {
if(i>=list.size()-3)
diPai.add(pai);
else if(i%3==0)
player01.add(pai);
else if(i%3==1)
player02.add(pai);
else if(i%3==2)
player03.add(pai);
i++;
}
//对玩家(底牌)的牌按从小到大排序
/*Collections.sort(player01);
Collections.sort(player02);
Collections.sort(player03);
Collections.sort(diPai);*/
System.out.println("玩家1:");
for (int p1 : player01) {
System.out.print(pokers.get(p1)+"\t");
}
System.out.println("\n玩家2:");
for (int p2 : player02) {
System.out.print(pokers.get(p2)+"\t");
}
System.out.println("\n玩家3:");
for (int p3 : player03) {
System.out.print(pokers.get(p3)+"\t");
}
System.out.println("\n底牌:");
for (int d : diPai) {
System.out.print(pokers.get(d)+"\t");
}
}
}
=======================输出结果===================================
玩家1:
方片3 红桃4 黑桃5 梅花5 方片5 梅花6 黑桃8 梅花8 方片8 黑桃9 红桃9 红桃Q 方片Q 红桃K 梅花K 红桃2 大王
玩家2:
红桃3 方片4 红桃6 黑桃7 红桃7 方片7 红桃8 梅花9 方片9 黑桃10 红桃10 黑桃J 红桃J 方片K 黑桃A 黑桃2 梅花2
玩家3:
黑桃3 黑桃4 梅花4 红桃5 黑桃6 方片6 梅花7 梅花10 方片10 梅花J 方片J 黑桃Q 梅花Q 黑桃K 红桃A 方片2 小王
底牌:
梅花3 梅花A 方片A