集合
集合
一、什么是集合
对象的容器,定义了对多个对象进行操作的常用方法。可实现数组的功能
- 和数组区别:
- 数组长度固定,集合长度不固定
- 数组可以存储基本类型和引用类型,集合只能存储引用类型
- 位置:java.util.*;
Colection体系集合
Collection父接口
代表一组任意类型的对象,无序、无序、不能重复
retainAll(Collection<?> c);保留交集
第一种用法
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
public class asd {
public static void main(String[] args) {
Collection collection=new ArrayList();
collection.add("苹果");
collection.add("西瓜");
System.out.println(collection);
for (Object object:collection
) {
System.out.println(object);
}
System.out.println(collection);
collection.remove("苹果");
System.out.println(collection);
System.out.println(collection.contains("西瓜"));
//迭代器(专门遍历集合的)
Iterator iterator=collection.iterator();
while(iterator.hasNext()){
String object = (String)iterator.next();
System.out.println(object);
//迭代器中不能用collection的remove方法删除,会出现并发删除的错误
//collection.remove(s);这句话是错误的
iterator.remove();
}
System.out.println(collection.contains("西瓜"));
System.out.println(collection);
collection.clear();
System.out.println(collection);
}
}
第二种用法
public class asd {
public static void main(String[] args) {
Collection collection =new ArrayList();
collection.add(new Student(1,"张三"));
collection.add(new Student(2,"李四"));
System.out.println(collection.toString());
collection.clear()只是移出去了,并没有删除掉对象,因为集合里面存的是引用的地址。
}
}
public class Student implements TestInterface {
private int age;
private String name;
public int getAge() {
return age;
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
List接口
有序,有下标元素可以重复
方法:
新的迭代器
ListIterator listIterator();比Iterator的方法多
新的remove(int index)根据下标删除
基本使用
public class asd {
public static void main(String[] args) {
List list=new ArrayList<>();
list.add("苹果");
list.add("华为");
list.add("小米");
System.out.println(list.size());
System.out.println(list.toString());
list.remove(0);
System.out.println(list.toString());
//遍历可以使用for ,增强for还有迭代器,还有list迭代器
//listIterator强大的地方,它可以向前遍历或向后遍历,可以删除修改增加元素
ListIterator listIterator=list.listIterator();
while(listIterator.hasNext()){
System.out.println(listIterator.nextIndex()+":"+listIterator.next());
}
while(listIterator.hasPrevious()){
System.out.println(listIterator.previousIndex()+":"+listIterator.previous());
}
System.out.println(list.contains("华为"));
System.out.println(list.isEmpty());
System.out.println(list.indexOf("华为"));
}
}
输出结果
3
[苹果, 华为, 小米]
[华为, 小米]
0:华为
1:小米
1:小米
0:华为
true
false
0
第二种
public class asd {
public static void main(String[] args) {
List list=new ArrayList();
//自动装箱
list.add(20);
list.add(30);
list.add(40);
list.add(50);
list.add(60);
list.remove(0);//或者使用list.remove((Object)20);
System.out.println(list.toString());
System.out.println(list.subList(1,3).toString()); ;
}
}
List实现类
ArrayList的使用
数组结构实现,查询快,增删慢
JDK1.2 运行效率快、线程不安全。
//测试类
public class asd {
public static void main(String[] args) {
ArrayList arrayList=new ArrayList();
Student s1=new Student(20,"刘德华");
Student s2=new Student(22,"郭富城");
Student s3=new Student(24,"大大");
arrayList.add(s1);
arrayList.add(s2);
System.out.println(arrayList.size());
System.out.println(arrayList.toString());
arrayList.remove(s1);
System.out.println(arrayList.size());
arrayList.remove(new Student(20,"刘德华"));//这么写必须在Student中重写equals方法,具体方法在下面
}
}
//学生类
public class Student implements TestInterface {
private int age;
private String name;
public int getAge() {
return age;
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);//返回的值的判定可以按照自己的意思来
//改了equals之后 contains这个方法因为源码中也是用equals来比较所以改了equals的话contains也被改变了,indexOf也是,可以使用new来比较
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
}
ArrayList源码解析
实现了list接口
静态常量:
DEFAULT_CAPACITY 默认的 容量大小,为10 ,如果没想集合中添加元素时,容量是0
elementData 存放元素的数组
size 实际的元素个数
构造方法
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//空数组
}
add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
add方法的解析
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//修改次数
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//右移一位,例如10右移一位也就是除以2等于5
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)//非常大的一个数,是Integer.MAX_VALUE-8
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
也就是每次扩容都是原来的1.5倍
Vector
数组结构实现,查询快、增删慢
JDK1.0版本,运行效率慢、线程安全
可以实现可增长的对象数组,与数组一样。
JDK1.2之后也实现了List接口
elements()枚举器
- hasMoreElements()
- nextElements()
public class asd {
public static void main(String[] args) {
Vector vector=new Vector();
vector.add("草莓");
vector.add("芒果");
vector.add("西瓜");
System.out.println(vector.size());
// vector.remove(0);
// vector.remove("西瓜");
// vector.clear();
Enumeration en=vector.elements();
while(en.hasMoreElements()){
String o=(String) en.nextElement();
System.out.println(o);
}
System.out.println(vector.contains("西瓜"));
System.out.println(vector.firstElement());;
}
}
LinkedList
链表结构实现,增删快,查询慢
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
st() {
}
public void addFirst(E e) {
linkFirst(e);
}
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #add}.
*
* @param e the element to add
*/
public void addLast(E e) {
linkLast(e);
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
private static class Node<E> {
E item;//当前元素,实际的数据
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
LinkList和ArrayList的区别
泛型
·Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作
为参数传递。
常见形式有泛型类、泛型接口、泛型方法。
语法∶
·<T,…> T称为类型占位符,表示一种引用类型。
好处∶
- 提高代码的重用性
- 防止类型转换异常,提高代码的安全性
//MyGeneric.java
public class MyGeneric<T> {
//使用泛型创建变量
T t;
public void show(T t){
System.out.println(t);
}
public T getT(){
return t;
}
}
//TestGeneric.java
public class TestGeneric {
public static void main(String[] args) {
MyGeneric<String> myGeneric =new MyGeneric<String >();
myGeneric.t="测试";
myGeneric.show("大家好");
String string=myGeneric.getT();
System.out.println(string);
}
}
泛型接口
//MyInterfac.java
public interface MyInterface<T> {
String name="张三";
//不能创建的静态常量
T service(T t);
}
//MyInterfaceImpl.java
public class MyInterfaceImpl implements MyInterface<String> {
@Override
public String service(String s) {
System.out.println(s);
return s;
}
}
//TestMyInterface
public class TestMyInterface {
public static void main(String[] args) {
MyInterface<String> myInterface=new MyInterfaceImpl();
myInterface.service("测试");
}
}
泛型方法
public class MyGenericMethod {
public <T> T show(T t) {
System.out.println(t);
System.out.println("泛型方法");
return t;
}
}
泛型的好处就是可以将重载简化了,不用写特别多的重载
泛型集合
·概念∶参数化类型、类型安全的集合,强制集合元素的类型必须一致。
·
特点∶
·编译时即可检查,而非运行时抛出异常。
访问时,不必类型转换(拆箱)。
不同泛型之间引用不能相互赋值,泛型不存在多态。
Set集合
特点:无序、无下标、元素不可重复
方法:全部继承自Collection继承过来的方法
不包含重复元素的Collection
- HashSet【重点】∶
基于HashCode实现元素不重复。 当存入元素的哈希码相同时,会调用==或equals进行确认,结果为true,拒绝后者存入。。 - LinkedHashSet: ·链表实现的HashSet,按照链表进行存储,即可保留元素的插入顺序。
- TreeSet: ·基于排列顺序实现元素不重复。 ·实现了SortedSet接口,对集合元素自动排序。 ·元素对象的类型必须实现Comparable接口,指定排序规则。·通过CompareTo方法确定是否为重复元素。
public class Demo1 {
public static void main(String[] args) {
Set<String> strings=new HashSet<>();
strings.add("华为");
strings.add("小米");
strings.add("苹果");
strings.add("小米");
System.out.println(strings.size());
System.out.println(strings.toString());
strings.remove("小米");
System.out.println(strings);
//遍历 1.增强for 2.迭代器
for (String s:strings){
System.out.println(s);
}
Iterator<String> it=strings.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
System.out.println(strings.contains("华为"));
}
}
输出
3
[苹果, 华为, 小米]
[苹果, 华为]
苹果
华为
苹果
华为
true
Process finished with exit code 0
HashSet
存储结构是哈希表
- 基于HashCode实现元素不重复。
- 当存入元素的哈希码相同时,会调用==或equals进行确认,结果为true,拒绝后者存入。。
public class Demo1 {
public static void main(String[] args) {
HashSet<String> hashSet=new HashSet<String>();
hashSet.add("天");
hashSet.add("地");
hashSet.add("云");
hashSet.add("月");
hashSet.add("月");
System.out.println(hashSet.size());
System.out.println(hashSet.toString());
hashSet.remove("云");
System.out.println(hashSet);
//遍历1.增强for 2.Iterator
Iterator iterator=hashSet.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
输出结果:
4
[地, 云, 月, 天]
[地, 月, 天]
地
月
天
HashCode的存储过程
- hashcode计算保存的位置,如果位置为空,则直接保存,若是不为空执行第二步
- 在执行equals方法,如果equals为true认为是同一个元素,否则形成链表。
可以重写equals和hashcode方法来达到对象属性的比较
TreeSet(红黑树)
特点:
- 基于排列顺序实现元素不重复
- 实现了SortedSet接口,对集合元素自动排序
- 元素对象的类型必须实现Comparable接口,指定排序规则
- 通过CompareTo方法确定是否为重复元素
public class Demo1 {
public static void main(String[] args) {
TreeSet<String> treeSet=new TreeSet<>();
treeSet.add("xyz");
treeSet.add("abc");
treeSet.add("hello");
System.out.println(treeSet.size());
System.out.println(treeSet.toString());
treeSet.remove("hello");
System.out.println(treeSet.toString());
}
}
3
[abc, hello, xyz]
[abc, xyz]
。TreeSet比较的规则,要实现Comparable接口
package com.city.test;
public class Person implements Comparable<Person>{
int age;
String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int compareTo(Person o) {
int n1=this.getAge()-o.getAge();
int name=this.getName().compareTo(o.getName());
return name==0?n1:name;
//先按姓名后按年龄
}
}
//如上代码写完后才可以将person放进treeset中
或者通过为TreeSet直接实现定制比较(比较器)Comparator
//运用了匿名内部类的方式实现
public class Demo1 {
public static void main(String[] args) {
TreeSet<Person> people=new TreeSet<>(new Comparator<Person>(){
@Override
public int compare(Person o1, Person o2) {
int age=o1.getAge()-o2.getAge();
int name=o1.getName().compareTo(o2.getName());
return age==0?name:age;
}
});
}
}
Map体系集合
方法
boolean containKey(Object Key);
boolean containValue(Object value);
Set<Map.Entry<K,V>> EntrySet()
Set keySet();
package com.city.test;
import java.util.*;
public class Demo1 {
public static void main(String[] args) {
//创建Map集合
Map<String ,String> map=new HashMap<>();
map.put("cn","中国");
map.put("UK","英国");
map.put("USA","美国");
System.out.println(map.toString());
map.remove("USA");
System.out.println(map.toString());
//遍历 1.keySet() 返回值是key的Set集合 2.EntrySet()方法
Set<String> set=map.keySet();
for (String key: set
) {
System.out.println(key+map.get(key));
}
Set<Map.Entry<String,String>> entries=map.entrySet();
for (Map.Entry<String,String> entry: entries
) {
System.out.println(entry.getValue());
System.out.println(entry.getKey());
}
}
}
Map集合的实现类
HashMap
构造一个具有默认初始容量(16)和默认加载因子(0.75)的空HashMap
//加载因子就是当达到最大容量的百分之75就扩容
源码分析
DEFAULT_INITIAL_CAPACITY 默认初始容量大小 1<< 4 ,是在添加第一个元素之后变大,为了节省空间
MAXIMUM_CAPACITY //最大的容量大小 1<<30
DEFAULT_LOAD_FACTOR 加载因子 0.75f,之后每次调整2倍
TREEIFY_THRESHOLD 默认为8 当链表的长度大于8的时候 并且数组的长度大于等于64,把链表编程红黑树。
UNTREEIFY_THRESHOLD 默认值为6 小于6没必要成为红黑树,就变成链表
table 哈希数组
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//计算位置用的,产生哈希值用的
}
- HashMap刚创建时,table是null,为了节省空间,当添加第一个元素时,table容量调整为16.
- 当元素个数大于阈值(16*0.78=12)时,会进行扩容,扩容后大小为原来的2倍,目的是减少调整元素的个数。
- jdk1.8,当每个链表长度大于8,并且元素个数大于等于64时,会调整为红黑树,目的是为了提供执行效率
- jdk、1.8 当链表长度小于6时调整为链表
- jdk1.8以前,链表时头插入,jdk1.8为尾插入
- hashset 创建的时hashmap,用Key来保存数据
binarySearch
二分查找,需要先排序才可以二分查找
copy
赋值,第一个参数是目标,第二个是数据,要求两个大小必须一样,可以先给目标集合赋值0或者对应的类型,是因为集合只有在有第一个值之后才会有大小
toArray()
list.toArray(new Integer[0]);
asList
Arrays.asList();
将数组变为集合,所转成的集合是受限的集合,不能添加和删除