01Java集合List篇
简述:集合是什么呢 是用来存储对象存储数据的一种方式能够存储任意长度的对象,长度可以随着元素的增加而增加,随着元素的减少而减少,使用起来方便一些。
集合VS数组:
区别1:
数组既可以存储基本数据类型,又可以存储引用数据类型,基本数据类型存储的是值,引用数据类型存储的是地址值
集合只能存储引用数据类型(对象),如果存储基本数据类型时,会自动装箱变成相应的包装类
区别2:
数组长度是固定的,不能自动增长
集合的长度的是可变的,可以根据元素的增加而自动增长
一.集合框架图
简化图
简单解释下上图:
1.所有集合类都是在java.util包下,Java集合类主要由两个派生接口,分别是Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口有他们的实现类和一些接口。
2.Collection接口是一组允许重复的对象
3.set和List接口继承Collection接口,set集合元素不重复,List允许重复,维护元素插入顺序
4.Map接口是键值对象,与Collection没有关系,不是Collection的子接口
5.Set,LIst,Map可以看做集合的三大类
Set集合是无序集合,集合中的元素不可以重复,访问集合中的元素可以根据元素本身访问(也是集合元素不允许重复的原因) 主要的实现类有HashSet和TreeSet
HashSet依赖于HashMap TreeSet依赖于TreeMap
List集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引访问。
Map集合中保存Key—Value对形式的元素,访问时根据key值来得到value值
6.Iterator是遍历集合的工具,遍历集合的方式我们主要用迭代器iterator来,
我们说Collection依赖于Iterator,是因为Collection的实现类要实现iterator方法。
LInkIterator是专门为遍历list存在的。
7.Arrays和Collection是工具类,是用来操作数组和集合的
1.LIst
说明:List接口继承Collection接口,它可以定义一个允许重复的有序集合,通过索引来访问List中的元素,实现类有LinkedList,ArrayList,Vector,Stack。
(1)ArrayList 是一个动态的集合 是我们常用的集合方式,可以存放null值,
每一个ArrayList都有一个初始容量(10),该容量代表了数组的大小,但是随着容器中的元素不断添加,容器的大小也会添加。也正是因为底层是一个数组,所以它的查询效率相比LinkedList快。Array List是非同步的
使用集合存储String类型
package cn.gok.tech01;
import java.util.ArrayList;
import java.util.Collection;
/**
* Collection常用的方法
* @author 杨潮
*
*/
public class TestString {
public static void main(String[] args) {
//多态
Collection collection=new ArrayList<>();
//add添加方法
collection.add("jQuery");
collection.add("ajax");
collection.add("json");
//获取元素的个数 size()是集合的长度
System.out.println(collection.size());//3
System.out.println(collection);//[jQuery ajax json]
//删除指定元素
collection.remove("ajax");
System.out.println(collection);//[jQuery,json]
//遍历方式 将集合转换为数组遍历输出
Object[] objects = collection.toArray();
for(int i=0;i<objects.length;i++){
System.out.println(objects[i]);//jQuery json
}
//清空集合
collection.clear();
//contains 是否包含
System.out.println(collection.contains("json"));//false
//isEmpty 是否为空
System.out.println(collection.isEmpty());//true
}
}
(2)LInkedLIst 是一个双向链表,与ArrayList不同的是,LInkedList不能随机访问,它需要根据链表的形式去执行,在列表中索引的操作将从开头或结尾遍历列表。所以LInkedLIst的插入和删除操作的效率相比ArrayList快。非同步的
(3)Vector 与ArrayList相似,但是Vector是同步的,是线程安全的动态数组,操作也与ArrayList相似
(4)Stack 是继承Vector 实现了一个后进先出的堆栈,Stack提供了五个方法,
基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的元素。
02Java集合Set篇
Set的特点
Set里面存储的元素不能重复,没有索引,存取顺序不一致。
public static void test1() {
HashSet<String> hs = new HashSet<>();
boolean b1 = hs.add("a");
boolean b2 = hs.add("a");
hs.add("d");
hs.add("c");
hs.add("b");
System.out.println(hs);//[a, b, c, d]
System.out.println(b1);//true
System.out.println(b2);//false
for (String string : hs) {
System.out.println(string);//abcd
}
}
存放自定义的对象
public static void test2() {
HashSet<Student> hs = new HashSet<>();
hs.add(new Student("张三", 20));
hs.add(new Student("张三", 20));
hs.add(new Student("李四", 22));
hs.add(new Student("李四", 22));
hs.add(new Student("李四", 22));
for (Student p : hs) {
System.out.println(p);
/**
* Student [name=李四, age=22]
* Student [name=张三, age=20]
*/
}
}
注意:在向HashSet中存放自定义类型对象时,一定要重写hashCode和equals方法
LinkedList
特点:他的底层使用了链表的数据结构,因此LinkedHashSet的特点是读取元素的顺序跟存入元素的顺序是一致的,并且元素不能重复
public static void test3() {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("g");
list.add("a");
list.add("b");
list.add("d");
list.add("b");
list.add("b");
list.add("c");
list.add("c");
System.out.println(list);//[a, g, a, b, d, b, b, c, c]
System.out.println("去除重复后:");//去除重复后:
LinkedHashSet<String> set2 = new LinkedHashSet<>();
//将list集合中的所有元素添加到set2
set2.addAll(list);
list.clear();
//将去除重复的元素添回到list中
list.addAll(set2);
System.out.println(list);//[a, g, b, d, c]
}
使用TreeSet存储Integer对象
TreeSet的特点是可以对存放进去的元素进行排序
public static void test4() {
TreeSet<Integer> ts = new TreeSet<>();
ts.add(3);
ts.add(3);
ts.add(3);
ts.add(1);
ts.add(1);
ts.add(5);
ts.add(2);
ts.add(4);
ts.add(2);
System.out.println(ts); //[1, 2, 3, 4, 5]
}
TreeSet存储对象类型
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", 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;
Person other = (Person) 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 int compareTo(Person o) {
int num = this.name.compareTo(o.getName());
if(num != 0){
return num;
}else{
//如果姓名排序一样则以age进行排序
return this.age - o.getAge();
}
}
}
测试
public static void test5() {
TreeSet<Person> ts = new TreeSet<>();
ts.add(new Person("tony", 23));
ts.add(new Person("paul", 20));
ts.add(new Person("lucy", 22));
ts.add(new Person("james", 24));
ts.add(new Person("lucy", 21));
System.out.println(ts);
//[Person [name=james, age=24], Person [name=lucy, age=21],
// Person [name=lucy, age=22], Person [name=paul, age=20], Person [name=tony, age=23]]
}
compareTo方法的返回值
TreeSet使用了二叉树的数据结构,负数放到左边,正数放到右边。
当compareTo方法返回0的时候,系统会认为两者一致,所以不会向集合中添加元素
当compareTo方法返回正数的时候,系统将元素存储到右边,所以集合存取顺序一致
03Java集合Map篇
Map接口概述
除了Collection之外,常用的集合还有Map接口,里面常用的实现类图如下
map中的元素是以键-值的方式存在的,通过键可以获取到值,键是不可以重复的,跟地图比较像,通过一个坐标就可以找到具体的位置。
public static void test6() {
Map<Integer,String> map = new HashMap<>();
String s1 = map.put(1001, "swag");
map.put(1002, "tony");
map.put(1003, "jack");
String s2 = map.put(1001, "jackson");
System.out.println(s1);//null
System.out.println(s2);//swag
//判断是否包含传入的键
System.out.println(map.containsKey(1002));//true
//判断是否包含传入的值
System.out.println(map.containsValue("jackson"));//true
//获取map的大小
System.out.println(map.size());//3
//将map中的值返回
Collection<String> c = map.values();
System.out.println(c);//[jackson, tony, jack]
System.out.println(map);//{1001=jackson, 1002=tony, 1003=jack}
//删除键是1001的数据并且将值返回
String s3 = map.remove(1001);
System.out.println(s3);//jackson
}
方式一:Map中的keySet()返回的是一个包含所有键的Set类型的对象,通过键获取值
public static void test7() {
Map<Integer, String> map = new HashMap<>();
map.put(1001, "swag");
map.put(1002, "tony");
map.put(1003, "jackson");
map.put(1004, "michael");
// 通过键获取值
String s = map.get(1003);
System.out.println(s);//jackson
// 获取map中所有的键返回给Set
Set<Integer> keySet = map.keySet();
//遍历set获取键,根据键获取值
Iterator<Integer> iter = keySet.iterator();
while(iter.hasNext()){
Integer key = (Integer)iter.next();
System.out.println("键:" + key + ",值:" + map.get(key));
}
for(Integer key : map.keySet()) {
System.out.println("键:" + key + ",值:" + map.get(key));
}
/**
* 键:1001,值:swag
键:1002,值:tony
键:1003,值:jackson
键:1004,值:michael
*/
}
方式二:Map中的键和值被封装成了Entry对象,并存储在Set集合中,通过entrySet()可以获取到这个Set集合。
public static void test8() {
Map<Integer, String> map = new HashMap<>();
map.put(1001, "swag");
map.put(1002, "tony");
map.put(1003, "jackson");
map.put(1004, "michael");
//键和值被封装成了Entry对象,并存储在Set集合中
Set<Map.Entry<Integer,String>> entrySet = map.entrySet();
Iterator<Map.Entry<Integer,String>> it = entrySet.iterator();
while(it.hasNext()) {
//获取每一个Entry对象
Entry<Integer,String> en = it.next();
//根据键值对对象获取键
Integer key = en.getKey();
//根据键值对对象获取值
String value = en.getValue();
System.out.println("键:" + key + ",值:" + value);
}
// 键:1001,值:swag
// 键:1002,值:tony
// 键:1003,值:jackson
// 键:1004,值:michael
//增强for循环
for (Entry<Integer, String> en : map.entrySet()) {
System.out.println("键:" + en.getKey() + ",值:" + en.getValue());
}
LinkedHashMap
LinkedHashMap的特点:存取顺序一致
public static void test9() {
LinkedHashMap<Integer,String> map = new LinkedHashMap<>();
map.put(1001, "swag");
map.put(1002, "tony");
map.put(1003, "jackson");
map.put(1004, "michael");
System.out.println(map);
}
TreeMap
TreeMap的特点:可以对存储的元素进行排序
public static void test10() {
TreeMap<Integer,String> map = new TreeMap<>();
map.put(1003, "jackson");
map.put(1004, "michael");
map.put(1001, "swag");
map.put(1002, "tony");
//{1001=swag, 1002=tony, 1003=jackson, 1004=michael}
System.out.println(map);
}
HashMap和Hashtable的区别:线程安全性,同步(synchronization),以及速度。
1.Hashtable是JDK1.0版本出现的,是线程安全的,效率低,不可以存储null键和null值
HashMap是JDK1.2版本出现的,可以存储null键和null值
2.HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
3.另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
4. 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
HashMap不能保证随着时间的推移Map中的元素次序是不变的。