四、容器
1. 数组
是一个简单的线性序列,速度快效率高。但是不够灵活,比如容量。
- Set没有顺序不可重复。
- List有顺序可重复。
- Map存放键值对。
2. 泛型
泛型的本质是数据类型的参数化,可以把泛型理解为一个形参,在调用泛型时必须传入实际类型。
class Solution {
public static void main(String[] args) {
MyCollection<String> mc = new MyCollection<>();
mc.set("tsc",0);
mc.set("123",1);
String b = mc.get(0);
System.out.println(b);
}
}
class MyCollection<E>{
Object[] objs = new Object[5];
public void set(E obj, int index){
objs[index] = obj;
}
public E get(int index){
return (E) objs[index];
}
}
实际上容器使用的接口在定义的时候使用的都是泛型。
3. Collection接口
class Solution {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
System.out.println(c.size());
System.out.println(c.isEmpty());
c.add("tsc");
c.add("abc");
System.out.println(c);
//这里是把该对象地址移除出容器,但是对象还在
c.remove("abc");
System.out.println(c);
Object[] objs = c.toArray();
System.out.println(objs);
System.out.println(c.contains("tsc"));
//移除所有元素
c.clear();
System.out.println(c);
}
}
4. ArrayList
class Solution {
public static void main(String[] args) {
List<String> list01 = new ArrayList<>();
list01.add("aa");
list01.add("bb");
list01.add("cc");
List<String> list02 = new ArrayList<>();
list02.add("aa");
list02.add("dd");
list02.add("ee");
System.out.println("list01:" + list01);
list01.addAll(list02);
System.out.println("list01:" + list01);
list01.retainAll(list02);
System.out.println("list01:" + list01);
list01.removeAll(list02);
System.out.println("list01:" + list01);
list01.addAll(list02);
System.out.println(list01.containsAll(list02));
}
}
List是有序,可重复的容器。
- 有序:List中的每个元素都有索引标记,可以根据元素的索引标记访问元素,从而精确控制这些元素。
- 可重复:允许加入重复的元素。
List常用的实现类有,ArrayList,LinkedList和Vector。
class Solution {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("A");
list.add("A");
list.add("A");
System.out.println(list);
//索引相关方法
list.add(2,"B");
System.out.println(list);
list.remove(3);
System.out.println(list);
list.set(2,"C");
System.out.println(list);
System.out.println(list.get(2));
list.add("C");
System.out.println(list);
System.out.println(list.indexOf("C"));
System.out.println(list.lastIndexOf("C"));
}
}
ArrayList底层是用数组实现的,查询效率高,增删改效率低,线程不安全。一般使用它,因为查询的情况多。
数组长度是有限的,ArrayList可以存放任意数量的对象,采取的是数组扩容的技术。定一一个更长的数组(原来的1.5倍)把原来的拷过来,新的加在后面。
ArrayList添加方法
public class MyArrayList<E> {
private Object[] elementData;
private int size;
private static final int DEFALT_CAPACITY = 10;
public MyArrayList(){
elementData = new Object[DEFALT_CAPACITY];
}
public MyArrayList(int capacity){
elementData = new Object[capacity];
}
public void add(E obj){
elementData[size ++] = obj;
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("[");
for(int i = 0 ; i < size ; i ++){
sb.append(elementData[i] + ",");
}
sb.setCharAt(sb.length() - 1,']');
return sb.toString();
}
public static void main(String[] args) {
MyArrayList m1 = new MyArrayList(20);
m1.add("a");
m1.add("b");
System.out.println(m1);
}
}
数组扩容
public class MyArrayList<E> {
private Object[] elementData;
private int size;
private static final int DEFALT_CAPACITY = 10;
public MyArrayList(){
elementData = new Object[DEFALT_CAPACITY];
}
public MyArrayList(int capacity){
elementData = new Object[capacity];
}
public void add(E obj){
//什么时候扩容
if (size == elementData.length){
//扩容操作
Object[] newArray = new Object[elementData.length + (elementData.length >> 1)];
System.arraycopy(elementData,0,newArray,0,elementData.length);
elementData = newArray;
}
elementData[size ++] = obj;
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("[");
for(int i = 0 ; i < size ; i ++){
sb.append(elementData[i] + ",");
}
sb.setCharAt(sb.length() - 1,']');
return sb.toString();
}
public static void main(String[] args) {
MyArrayList m1 = new MyArrayList(20);
m1.add("a");
m1.add("b");
System.out.println(m1);
for (int i = 0; i < 40; i++) {
m1.add("aa");
}
System.out.println(m1);
}
}
增加set,get方法
public class MyArrayList<E> {
private Object[] elementData;
private int size;
private static final int DEFALT_CAPACITY = 10;
public MyArrayList(){
elementData = new Object[DEFALT_CAPACITY];
}
public MyArrayList(int capacity){
elementData = new Object[capacity];
}
public void add(E obj){
//什么时候扩容
if (size == elementData.length){
//扩容操作
Object[] newArray = new Object[elementData.length + (elementData.length >> 1)];
System.arraycopy(elementData,0,newArray,0,elementData.length);
elementData = newArray;
}
elementData[size ++] = obj;
}
public E get(int index){
if (index < 0 || index > size - 1){
throw new RuntimeException("索引不合法");
}
return (E)elementData[index];
}
public void set(E element, int index){
if (index < 0 || index > size - 1){
throw new RuntimeException("索引不合法");
}
elementData[index] = element;
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("[");
for(int i = 0 ; i < size ; i ++){
sb.append(elementData[i] + ",");
}
sb.setCharAt(sb.length() - 1,']');
return sb.toString();
}
public static void main(String[] args) {
MyArrayList m1 = new MyArrayList(20);
m1.add("a");
m1.add("b");
System.out.println(m1);
for (int i = 0; i < 40; i++) {
m1.add("aa");
}
System.out.println(m1);
System.out.println(m1.get(4));
m1.set("bb",11);
System.out.println(m1);
}
}
remove
public void remove(E element){
//element将它和所有元素比较,获得第一个为true的返回
for (int i = 0; i < size; i++) {
if (element.equals(get(i))){ //容器中所有的比较操作,都是用equals而不是==
//将该元素从此处移除
remove(i);
}
}
}
public void remove(int index){
int numMoved = elementData.length - index - 1;
if (numMoved > 0){
System.arraycopy(elementData,index + 1,elementData,index,numMoved);
elementData[size - 1] = null;
}
if (numMoved == 0){
elementData[size - 1] = null;
}
size --;
}
public int size(){
return size;
}
public boolean isEmpty(){
return size == 0 ? true : false;
}
5. LinkedList
底层是用双向链表实现的存储,查询效率低,增删效率高,线程不安全。
双向链表结构:
class Node {
Node previous;
Object element;
Node next;
}
public class MyLinkedList<E> {
private Node first;
private Node last;
private int size;
public void add(Object obj){
Node node = new Node(obj);
if (first == null){
first = node;
last = node;
}else {
node.previous = last;
node.next = null;
last.next = node;
last = node;
}
size ++;
}
public void insert(int index, E obj){
Node newNode = new Node(obj);
Node temp = getNode(index);
if (index == 0){
first.previous = newNode;
newNode.next = first;
first = newNode;
size ++;
return;
}
if (index == size){
add(obj);
return;
}
if (temp != null){
Node up = temp.previous;
up.next = newNode;
newNode.previous = up;
newNode.next = temp;
temp.previous = newNode;
size ++;
}
}
public void remove(int index){
Node temp = getNode(index);
if (temp != null){
Node up = temp.previous;
Node down = temp.next;
if (up != null){
up.next = down;
}
if (down != null){
down.previous = up;
}
if (index == 0){
first = down;
}
if (index == size - 1){
last = up;
}
size --;
}
}
public E get(int index){
if (index < 0 || index > size - 1){
throw new RuntimeException("索引不合法");
}
Node temp = getNode(index);
return (E)temp.element;
}
public Node getNode(int index){
Node temp = null;
if (index < (size >> 1)) {
temp = first;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
}else {
temp = last;
for (int i = size - 1; i > index ; i--) {
temp = temp.previous;
}
}
return temp;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
Node temp = first;
while (temp != null){
sb.append(temp.element + ",");
temp = temp.next;
}
sb.setCharAt(sb.length() - 1,']');
return sb.toString();
}
public static void main(String[] args) {
MyLinkedList list = new MyLinkedList();
list.add("a");
list.add("b");
list.add("c");
System.out.println(list);
System.out.println(list.get(1));
list.remove(1);
System.out.println(list);
list.insert(0,"d");
System.out.println(list);
list.insert(3,"k");
System.out.println(list);
}
}
6.Vector向量
底层是用数组实现的List,线程安全。相关方法都加了同步检查,线程安全效率低。比如indexof方法就加了synchronized同步标记。
- 需要线程安全的时候使用vector。
- 不存在线程安全问题时,查找较多使用ArrayList。
- 不存在线程安全问题时,增删较多使用LinkedList。
7.Map接口
存储的是键值对,通过键对象找值对象。
public class MyMap {
public static void main(String[] args) {
Map<Integer,String> m1 = new HashMap<>();
m1.put(1,"one");
m1.put(2,"two");
System.out.println(m1.get(1));
System.out.println(m1.size());
m1.remove(1);
System.out.println(m1.size());
System.out.println(m1.containsKey(2));
System.out.println(m1.containsValue("two"));
Map<Integer,String> m2 = new HashMap<>();
m2.put(3,"three");
m2.put(4,"four");
m1.putAll(m2);
System.out.println(m1.size());
//map中键不能重复,否则新的覆盖旧的
m1.put(3,"三");
System.out.println(m1);
}
}
常用方法
public class MyMap {
public static void main(String[] args) {
Employee e1 = new Employee(1,"tsc",20000);
Employee e2 = new Employee(2,"tscc",30000);
Employee e3 = new Employee(3,"tsccc",40000);
Map<Integer,Employee> map = new HashMap<>();
map.put(1,e1);
map.put(2,e2);
map.put(3,e3);
Employee emp = map.get(1);
System.out.println(emp.getName());
}
}
class Employee{
private int id;
private String name;
private double salary;
public Employee(int id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
8. HashMap
底层使用了哈希表,是一种非常重要的数据结构。本质上就是数组+链表。
key先通过hashcode()方法得到一个数字,这个数字再通过散列函数确定桶进行存储,初始的桶个数是16,大小是可变的。如果桶中的元素达到0.75*数组长度的时候就会调整成为原来的二倍。扩容是很耗时的,需要定义更大的数组并将旧数组内容拷贝到新数组中。
当每个桶中的链表长度大于8时,会变成红黑树。
public class MyHashMap {
Node2[] table; //桶数组
int size; //存放的键值对数
public MyHashMap(){
table = new Node2[16];
}
public void put(Object key,Object value){
//定义了新的节点对象
Node2 newNode = new Node2();
newNode.hash = myHash(key.hashCode(),table.length);
newNode.key = key;
newNode.value = value;
Node2 temp = table[newNode.hash];
Node2 last = null;
if (temp == null){
table[newNode.hash] = newNode;
}else {
while (temp != null){
//判断Key如果重复,则覆盖
if (temp.key.equals(key)){
temp.value = value; //只覆盖value
return;
}else {
//key不重复,
last = temp;
temp = temp.next;
}
}
last.next = newNode;
}
}
public Object get(Object key){
int hash = myHash(key.hashCode(), table.length);
Object value = null;
if (table[hash] != null){
Node2 temp = table[hash];
while (temp != null){
if (temp.key.equals(key)){
value = temp.value;
break;
}else {
temp = temp.next;
}
}
}
return value;
}
public int myHash(int v,int length){
return v & (length - 1);//和v%(length-1)作用一样(散列到0-15之间),但是值不一样。效率高
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder("{");
for (int i = 0; i < table.length; i++) {
Node2 temp = table[i];
while (temp != null){
sb.append(temp.key + ":" + temp.value + ",");
temp = temp.next;
}
}
sb.setCharAt(sb.length() - 1,'}');
return sb.toString();
}
public static void main(String[] args) {
MyHashMap m = new MyHashMap();
m.put(1,"aa");
m.put(2,"bb");
m.put(3,"cc");
m.put(3,"dd");
m.put(53,"53");
m.put(69,"69");
m.put(85,"85");
System.out.println(m.get(69));
System.out.println(m);
}
}
class Node2{
int hash;
Object key;
Object value;
Node2 next;
}
8.5 TreeMap
public class MyTreeMap {
public static void main(String[] args) {
Map<Integer,String> treemap1 = new TreeMap<>();
treemap1.put(20,"aa");
treemap1.put(3,"bb");
treemap1.put(6,"cc");
// 按照key递增的大小排序
for (Integer m : treemap1.keySet()){
System.out.println(treemap1.get(m));
}
}
}
// bb
// cc
// aa
9. Set
和List一样,继承了Collection接口,是没有顺序不可重复的。没有索引查找只能遍历,不允许加入重复的元素,新元素如果通过equals()方法对比为true则不能加入甚至也只能放入一个null元素不能多个。
public class MyHashSet {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("aa");
set.add("bb");
set.add("cc");
System.out.println(set);
set.remove("aa");
System.out.println(set);
Set<String> set2 = new HashSet<>();
set2.add("dd");
set2.addAll(set);
System.out.println(set2);
}
}
public class MyHashSet {
HashMap map;
private static final Object PRESENT = new Object();
public MyHashSet(){
map = new HashMap();
}
public void add(Object o){
map.put(o,PRESENT);
}
public int size(){
return map.size();
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("[");
for (Object key : map.keySet()){
sb.append(key + ",");
}
return sb.toString();
}
public static void main(String[] args) {
MyHashSet set = new MyHashSet();
set.add("aa");
set.add("bb");
System.out.println(set);
}
}
10. 迭代器Iterator
class Solution {
public static void main(String[] args) {
testIteratorSet();
}
public static void testIteratorList(){
List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
for (Iterator<String> iter = list.iterator();iter.hasNext();){
String temp = iter.next();
System.out.println(temp);
}
}
public static void testIteratorSet(){
Set<String> set = new HashSet<>();
set.add("aa");
set.add("bb");
set.add("cc");
for (Iterator<String> iter = set.iterator();iter.hasNext();){
String temp = iter.next();
System.out.println(temp);
}
}
}
class Solution {
public static void main(String[] args) {
testMap2();
}
public static void testMap(){
Map<Integer,String> map1 = new HashMap<>();
map1.put(1,"a");
map1.put(2,"b");
map1.put(3,"c");
Set<Map.Entry<Integer,String>> ss = map1.entrySet();
for (Iterator<Map.Entry<Integer,String>> iter = ss.iterator(); iter.hasNext();){
Map.Entry<Integer,String> temp = iter.next();
System.out.println(temp.getKey() + " : " + temp.getValue());
}
}
public static void testMap2(){
Map<Integer,String> map1 = new HashMap<>();
map1.put(1,"a");
map1.put(2,"b");
map1.put(3,"c");
Set<Integer> keySet = map1.keySet();
for (Iterator<Integer> iter = keySet.iterator();iter.hasNext();){
Integer key = iter.next();
System.out.println(key + " : " + map1.get(key));
}
}
}
11. Collections 辅助类的使用
class Solution {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add("t" + i);
}
System.out.println(list);
//随机排列
Collections.shuffle(list);
System.out.println(list);
//逆序
Collections.reverse(list);
System.out.println(list);
//递增排序
Collections.sort(list);
System.out.println(list);
//二分法查找
System.out.println(Collections.binarySearch(list,"t2"));
}
}