Java的集合框架:

  • Collection接口:
    • List:里面存放的数据是有序的,并且数据是可重复的

      • ArrayList:动态数组,顺序存储,遍历、添加(添加到末尾,非插入)效率较高
      • LinkedList:链式结构,插入、删除效率高,遍历查找效率低
      • Vector:动态数组,比较老的集合结构,一般很少使用
    • Set:里面的数据是无序的,不可重复的

      • HashSet:主要的实现类,无序不代表随机

        • LinkedHashSet : 是在原有的基础上底层通过一个链表进行维护,遍历效率会有提升

          ​ 若经常对set进行遍历操作,可以采用LinkedHashSet

      • TreeSet:TreeSet中存放的数据可以进行排序(Comparable Comparator)

        ​ TreeSet中存放的对象必须实现Comparable

        ​ TreeSet中是不能存放空对象的 !!!

  • Map接口:Map中存放的数据都是一对一对的,里面有一个叫做key,另外一个叫做value,键值对
    • HashMap:主要实现类,map中数据也是无序的,线程不安全,但是效率高
    • TreeMap:可以排序的
    • HashTable:比较老的一个,很少用,线程安全的,但是效率较低
      • Properties类 property(属性)

Collection接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Collection<String> coll = new ArrayList<>();

// 添加,添加的元素只能是指定类型的数据
coll.add("a");

// 返回集合中元素的个数
int num = coll.size();

// 将一个集合中的数据添加进去
coll.addAll(coll2);

// 清除集合中数据
coll2.clear();

// 是否包含指定元素
boolean bl = coll.contains("aq");

// 比较两个集合中的元素是否相等
System.out.println(coll2.equals(coll3));

// 判断集合是否为空
System.out.println(coll.isEmpty());

// 删除指定元素
coll.remove("a");

// 在当前集合中删除传入集合中包含的元素
coll.removeAll(coll2);

// 保留当前集合中两个集合的公共部分
coll.retainAll(coll2);

// 将集合中元素转换到数组中,括号中数组的类型即为元素类型,长度即为集合的元素的个数
String[] arr = coll.toArray(new String[coll.size()]);

// 集合中存储的是对象时,若想判断是否相等,则必须在对象的类中重写equals()方法
System.out.println(coll1.equals(coll2));

// 获取当前集合的迭代器对象(管家)
Iterator<String> iterator = coll.iterator();

// 这两个方法结合使用
// hasNext():判断后面是否还有元素
while(iterator.hasNext()) {
// next():返回下一个元素
System.out.println(iterator.next());
}

// 遍历过程中把某个元素删除
while (iterator.hasNext()) {
String str = iterator.next();
if (str.equals("jack")) {
// 通过迭代器对象删除
// 删除要在next()方法之后执行,next之后删除不能多次执行
iterator.remove();
}
}

List

ArrayList

ArrayList:动态数组,底层是由数组实现数据存储的

对于数据基本操作:

  • 增:add()
  • 删:remove(object) remove(index)
  • 改:set(index,element)
  • 查:get(index)
  • 插入:add(index,element)
  • 遍历:迭代器(next()、hasNext())、for、foreach

新增的:

  • add(index,element)
  • remove(index)
  • set(index,element)
  • get(index)
  • subList(startIndex,endIndex)
  • indexOf(object)
  • sort(Comparator) // 排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 创建ArrayList对象
ArrayList<String> list = new ArrayList<>();

// 在指定位置添加数据,下标不能越界
list.add(0, "爷爷");

// 获取指定位置的元素
System.out.println(list.get(3));

// 将指定位置的数据进行更新,参数(下标,新的值)
list.set(4, "盖伦");

// 返回对应数据在Arraylist中的下标,若没有返回-1
System.out.println(list.indexOf("大娃"));

// 删除指定位置的元素,并且会将该元素返回
list.remove(5);

// 截取集合中指定范围(0<= x < 2)元素,并以List形式返回,
List<String> data = list.subList(0, 2);

// 将集合中的元素拼接成字符串返回
System.out.println(list.toString());

ArrayList遍历方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1、迭代器
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}

// 2、for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}

// 3、foreach(依赖于迭代器)
for (String str : list) {
System.out.println(str);
}

注意点:遍历时要删除元素,采用迭代器形式,不要用foreach会报错

1
2
3
4
5
6
7
8
// 1、迭代器(可以的)
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()) {
String name = iterator.next();
if(name.equals("三娃")) {
iterator.remove();
}
}

排序:

1
2
3
4
5
6
7
8
9
// 对集合中元素进行排序,需要传入一个Comparator接口的对象
list.sort(new Comparator<String>() {
// 返回值: 小于0,o1<o2 ; 等于0,o1=o2 ;大于0,o1 > o2
@Override
public int compare(String o1, String o2) {
System.out.println("compare");
return o1.compareTo(o2);
}
});

LinkedList

LinkedList里面的常规增删改查方法与ArrayList一样

1
2
LinkedList<String> list = new LinkedList<>();
list.add("李白");

Vector

1
2
Vector<String> vector = new Vector<>();		
vector.add("abc");

三者区别

1.ArrayList

  • 创建ArrayList时,内部会生成一个空数组
  • 第一次添加时,数组长度为10
  • 后期数组满了后在扩容时,是扩容1.5倍
  • 线程不安全,效率高

2.Vector

  • 创建时立马生成长度为10的数组
  • 扩容时默认以2倍扩容
  • 线程安全,但是带来副作用就是效率低

3.LinkedList

  • 链式存储结构,双链表
  • 通过Node类表示数据结点,
    • 里面item属性用来保存具体的数据
      undefinednext属性保存下一个结点地址
      undefinedprev属性保存上一个结点地址

Set

HashSet

Set底层由数组+链表实现

  • 1.数据存放在数组中(数组的初始容量为16)
  • 2.无序性是根据存放对象的哈希值经过某个算法(散列算法)算出在数组中存放的位置,从而导致存放的数据不太可能连着存放,展现出来一种无序状态
  • 3.数据的不可重复需要:数据对象对hashcode().eauals()进行重写才能体现,否则还是可以添加重复的数据,经过hashcode()得到的哈希值算出存储的位置后:
    • 若数组当前位置没有数据,就直接放进去
      
    • 若数组当前位置有数据,就通过对象的equals()进行判断,
      * 若相等则不添加,
      * 若不相等,则以链表的形式拼接在原有数据下面;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
HashSet<String> set = new HashSet<>();

// 添加数据
set.add("hack");

// 相同数据不会添加成功
set.add("hack");

// HashSet可以添加null
set.add(null);

// set数据的获取只能通过迭代器
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}

// 集合的构造方法中可以传入另外一个Collection接口对象
ArrayList<String> list = new ArrayList<>();
HashSet<String> set = new HashSet<>(list);
System.out.println(set);

// 再把set转换成ArrayList
ArrayList<String> list2 = new ArrayList<>(set);

TreeSet

1
2
3
4
5
6
7
8
9
10
11
// TreeSet中是不能存放空对象的 !!!
set.add(null);

// 指定规则去排序、传入Comparator对象
TreeSet<User> set2 = new TreeSet<>(new Comparator<User>() {

@Override
public int compare(User o1, User o2) {
return o1.age - o2.age;
}
});

Map接口

###HashMap原理简要认识:

  • HashMap<String, String> map = new HashMap<>();
    1. 底层由 数组+链表+红黑树
    1. 创建HashMap时,内部存放键值对数据的数组table,没有初始化,只是初始化了一个加载因子为0.75
    • 加载因子:可以认为数组中元素个数达到当前数组长度的百分比后进行扩容一个参数
    1. 当第一次添加数据时,会把table这个数组初始化,长度为16
    1. 后续再去添加元素时,如果数组中元素的个数 = 数组长度*加载因子,那么就会进行扩容,扩容为原来的2倍
    1. 当数组长度大于64,并且数组中某个元素对应的链表长度大于8时,就会采用红黑树(二叉树)进行存储,提高查找的效率
1
2
3
4
5
6
7
/*
*增:put(key,value)
* 查:get(key)、containsKey(...)、containsValue(...)
* 删除:remove(key)
* 改:put(key,value)
* 长度:size()
*/

HashMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 第一个指定key的类型,第二个指定value的类型
HashMap<String, String> map = new HashMap<>();

// 添加数据,注意key和value的类型
map.put("name", "jack");

// 获取某个数据
System.out.println(map.get("name"));

// 清除map中的数据
map.clear();

// 特殊情况
// map中的key或者value都可以为null
map.put(null, "jack");

// 删除指定的key对应的数据
map.remove("sex");

// 检查map中是否包含指定的key 、value
System.out.println(map.containsKey("name"));
System.out.println(map.containsValue("jack"));

map的遍历:

1
2
3
4
5
6
7
8
9
10
// 方式1:获取key的set集合,然后遍历该集合获取map中的所有数据
// 返回一个包含key的set集合
Set<String> keys = map.keySet();
Iterator<String> iterator1 = keys.iterator();
while (iterator1.hasNext()) {
String key = iterator1.next();
String value = map.get(key);

System.out.println(key + " " + value);
}
1
2
3
4
5
6
7
// 方式2:
// values():反回一个包含所有value数据的集合
Collection<String> values = map.values();
Iterator<String> iterator2 = values.iterator();
while (iterator2.hasNext()) {
System.out.println(iterator2.next());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 方式3:
// entrySet():返回的是一个包含Entry对象的set集合
// Entry(1.8中叫Node):就是表示key-value键值对的类
Set<Entry<String, String>> entrySet = map.entrySet();
// 遍历
Iterator<Entry<String, String>> iterator3 = entrySet.iterator();
while (iterator3.hasNext()) {
// 获取键值对对象
Entry<String, String> entry = iterator3.next();
// 获取key和value
String key = entry.getKey();
String value = entry.getValue();

System.out.println(key + " " + value);
}

CurrentHashMap

由于HashMap是线程不同步的,虽然处理数据的效率高,但是在多线程的情况下存在着安全问题,因此设计了CurrentHashMap来解决多线程安全问题。

JDK1.8的实现已经摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap,虽然在JDK1.8中还能看到Segment的数据结构,但是已经简化了属性,只是为了兼容旧版本.

https://www.jianshu.com/p/a7767e6ff2a2

HashMap和ConcurrentHashMap的知识总结

HashMap和ConcurrentHashMap的知识总结

TreeMap

TreeMap中是按照key进行一个自然排序,要求key所对应的数据必须实现Comparable接口

1
2
3
TreeMap<Hero, String> treeMap = new TreeMap<>();
// key不实现Comparable接口就报错
// treeMap.put(new Hero("jack", 200, "学生"), "abc");
1
2
3
4
5
6
7
8
// 通过在构造方法中传入Comparator对象自定义比较规则,
// 那么key所对应的对象可以不用实现Comparable接口,即便实现了也不会生效
TreeMap<Hero, String> treeMap = new TreeMap<>(new Comparator<Hero>() {
@Override
public int compare(Hero o1, Hero o2) {
return o1.blood - o2.blood; // 按照血量
}
});

Properties

Properties:可以用来读取(加载)项目中的配置文件(例如:数据库的连接信息,用户名 密码 …)

创建配置文件(xxx.properties):右键单击项目名 -> new -> file

  • 井号代表注释
  • 内容格式:属性名=属性值 / 属性名:属性值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 创建Properties对象
Properties p = new Properties();

// 读取(加载)配置文件的数据
// 参数是一个输入流对象
// 文件是在当前项目的根路径下面,
// 创建输入流时,如果参数是文件的地址,默认情况下是在当前项目根路径下进行查找
p.load(new FileInputStream("db.properties"));

// 加载外部配置文件
// File file = new File("d:\\db.properties");
// p.load(new FileInputStream(file));

// 加载之后根据属性名获取属性值
String name = (String) p.get("name");
String pwd = (String) p.get("pwd");
String url = (String) p.get("url");

System.out.println(name);
System.out.println(pwd);
System.out.println(url);

总结

1、Collection 和 Collections

​ Collection是一个接口,是所有单值集合的父接口;

​ Collections是一个帮助类,这个类中提供了很多对于集合操作的静态方法

2、List接口 和 Set接口的区别

​ List接口和set接口都是Collection接口的子接口

​ List接口中的元素是有序可重复的;

​ Set接口中的元素是无序不可重复的;

3、ArrayList 和 LinkedList的区别

​ ArrayList和LinkedList集合都是List接口的实现类,其元素都是有序可重复的;

​ ArrayList中元素的存储是基于数组的实现,元素查询、添加速度比较快,插入元素速度较慢;

​ LinkedList中的元素是基于链表的实现,元素的插入速度较快;

4、ArrayList 和 Verctor的区别

​ ArrayList和Verctor都是基于数组的实现;

​ ArrayList是线程非安全的,但是ArrayList的存储效率比较高;

​ Verctor是线程安全的;

5、HashSet和TreeSet的区别

​ 两者都是Set接口的实现类,其元素都是无序不可重复的;

​ 区别是存储方式不同:

​ HashSet是基于hash码散列存储

​ TreeSet是树形存储

6、HashMap和HashTable的区别

​ 两者都是基于Map接口的实现类,都表示键值对集合。

​ HashMap中运行一个null键和多个null值;但Hashtable中不允许使用null作为key和value;

​ HashMap未实现同步,是线程非安全的;Hashtable实现了同步是线程安全的;