Java 方法篇

张天宇 on 2019-11-12

这篇主要记录了Java中的一些常用数据结构和方法。

List

增删查改

添加List.add、删除List.remove、查找List.get、更改List.set、存在List.contains

1
2
3
4
5
6
7
8
9
10
List<String> person = new ArrayList<>();
person.add("A");
person.add("B");
person.remove(1);
String result = person.get(0);
System.out.println(result); //A
person.set(0, "C");
for (String s : person)
System.out.println(s); //C
System.out.println(person.contains("C")); //true

改变与替换

.add将元素放到索引index的位置,原来位置的元素后移。.set替换index位置的元素。

1
2
person.set(1, "D");
person.add(0, "E");

查看索引

indexOflastIndexOf

1
2
3
4
5
6
7
person.clear(); //清空
person.add("A");
person.add("B");
person.add("A");
person.add("B");
System.out.println(person.indexOf("A")); //0
System.out.println(person.lastIndexOf("A")); //2

截取

subList截取区间 [fromIndex,toIndex) 作为新List。

1
2
3
4
5
6
7
8
9
person.clear();
person.add("A");
person.add("B");
person.add("C");
person.add("D");
person.add("E");
person = person.subList(1, 4);
for (String s : person)
System.out.println(s); // B C D

对比

equals对比两个对象,两个对象中的所有元素完全相同。

1
person.equals(fruits)

判空

isEmpty()

去重

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
List<String> lst1 = new ArrayList<>();
lst1.add("aa");
lst1.add("dd");
lst1.add("ss");
lst1.add("aa");
lst1.add("ss");

// 方法 1.
for (int i = 0; i < lst1.size() - 1; i++) {
for (int j = lst1.size() - 1; j > i; j--) {
if (lst1.get(j).equals(lst1.get(i))) {
lst1.remove(j);
}
}
}
System.out.println(lst1);

// 方法 2.
List<String> lst2 = new ArrayList<>();
for (String s : lst1) {
if (Collections.frequency(lst2, s) < 1) {
lst2.add(s);
}
}
System.out.println(lst2);

https://blog.csdn.net/Barcon/article/details/82628120

PriorityQueue

概述

优先队列PriorityQueue是Queue接口的实现,可以对其中元素进行排序,可以放基本数据类型的包装类(如:Integer,Long等)或自定义的类。对于基本数据类型的包装器类,优先队列中元素默认排列顺序是升序排列,但对于自己定义的类来说,需要自己定义比较器。

常用方法

1
2
3
4
5
peek()//返回队首元素
poll()//返回队首元素,队首元素出队列
add()//添加元素
size()//返回队列元素个数
isEmpty()//判断队列是否为空,为空返回true,不空返回false

使用

队列保存的是基本数据类型的包装类

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
//自定义比较器,降序排列
static Comparator<Integer> cmp = new Comparator<Integer>() {
public int compare(Integer e1, Integer e2) {
return e2 - e1;
}
};
public static void main(String[] args) {
//不用比较器,默认升序排列
Queue<Integer> q = new PriorityQueue<>();
q.add(3);
q.add(2);
q.add(4);
while(!q.isEmpty())
{
System.out.print(q.poll()+" ");
}
/**
* 输出结果
* 2 3 4
*/
//使用自定义比较器,降序排列
Queue<Integer> qq = new PriorityQueue<>(cmp);
qq.add(3);
qq.add(2);
qq.add(4);
while(!qq.isEmpty())
{
System.out.print(qq.poll()+" ");
}
/**
* 输出结果
* 4 3 2
*/
}

保存了自定义类

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
//矩形类
class Node{
public Node(int chang,int kuan)
{
this.chang=chang;
this.kuan=kuan;
}
int chang;
int kuan;
}

public class Test {
    //自定义比较类,先比较长,长升序排列,若长相等再比较宽,宽降序
static Comparator<Node> cNode=new Comparator<Node>() {
public int compare(Node o1, Node o2) {
if(o1.chang!=o2.chang)
return o1.chang-o2.chang;
else
return o2.kuan-o1.kuan;
}

};
public static void main(String[] args) {
Queue<Node> q=new PriorityQueue<>(cNode);
Node n1=new Node(1, 2);
Node n2=new Node(2, 5);
Node n3=new Node(2, 3);
Node n4=new Node(1, 2);
q.add(n1);
q.add(n2);
q.add(n3);
Node n;
while(!q.isEmpty())
{
n=q.poll();
System.out.println("长: "+n.chang+" 宽:" +n.kuan);
}
     /**
      * 输出结果
      * 长: 1 宽:2
      * 长: 2 宽:5
      * 长: 2 宽:3
      */
}
}

遍历

PriorityQueue的iterator()不保证以任何特定顺序遍历队列元素。若想按特定顺序遍历,先将队列转成数组,然后排序遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
Queue<Integer> q = new PriorityQueue<>(cmp);
int[] nums= {2,5,3,4,1,6};
for(int i:nums){
q.add(i);
}
Object[] nn=q.toArray();
Arrays.sort(nn);
for(int i=nn.length-1;i>=0;i--)
System.out.print((int)nn[i]+" ");
/**
* 输出结果
* 6 5 4 3 2 1
*/

比较器降序说明

1
2
3
4
5
6
7
8
Comparator<Object> cmp = new Comparator<Object>() {
public int compare(Object o1, Object o2) {
//升序
return o1-o2;
//降序
return o2-o1;
}
};

数据流中的中位数

下面再来看这道题

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

普通解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public:
vector<int> s;
void Insert(int num) { s.push_back(num); }

double GetMedian() {
sort(s.begin(), s.end());
int i = s.size();
if (i % 2 != 0)
return s[(i + 1) / 2 - 1];
else
return ((double)s[i / 2 - 1] + (double)s[i / 2]) / 2;
}
};

生成一个大顶堆和小顶堆,维持大顶堆的数都小于等于小顶堆的数,且二者个数相等或者相差为1,平均数即在两个栈顶的数之中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
// 右边小顶堆的元素都大于左边大顶堆的元素
// 并且左边元素的个数,要么与右边相等(偶数次输入),要么比右边大1(奇数次输入)
priority_queue<int, vector<int>, less<int> > left; //大顶堆
priority_queue<int, vector<int>, greater<int> > right; //小顶堆

public:
void Insert(int num) {
if (left.empty() || num <= left.top())
left.push(num);
else
right.push(num);
// 左边大顶堆的大小最多可以比右边小顶堆大1(奇数次输入)
if (left.size() == right.size() + 2) right.push(left.top()), left.pop();
// 因为最后返回的只有可能是左边大顶堆的堆顶,所以右边小顶堆的大小不能比左边小顶堆大
if (left.size() + 1 == right.size())
left.push(right.top()), right.pop();
}
double GetMedian() {
return left.size() == right.size() ? (left.top() + right.top()) / 2.0
: left.top();
}
};

StringBuffer

概述

当对字符串进行修改时候,需要使用StringBuffer和StringBuilder类。不同于String,他们可以被多次修改而不产生新的对象。StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

String适用于少量的字符串操作的情况,StringBuilder适用于单线程下在字符缓冲区进行大量操作的情况,StringBuffer适用多线程下在字符缓冲区进行大量操作的情况。

使用

toString()

1
2
StringBuffer s1 = new StringBuffer();
s1.toString();

append()

1
2
StringBuffer s1 = new StringBuffer().append("bbb");
s1.append("aaa"); //bbbaaa

charAt()

1
System.out.println(s1.charAt(3));	//a

deleteCharAt()

1
2
StringBuffer s1 = new StringBuffer().append("bbbaaa");
s1.deleteCharAt(3); //bbbaa

delete()

删除从开始索引到结束索引的字符串。

1
2
StringBuffer s1 = new StringBuffer().append("bbbaaa");
s1.delete(2, 4); //bbaa

insert()

在指定索引位置之前插入字符串。

1
2
StringBuffer s1 = new StringBuffer().append("bbbaaa");
s1.insert(2,"cc"); //bbccbaaa

indexOf()

返回指定字符串的开始字符索引位置,还可以从某个字符索引位置开始向后匹配,没有找到匹配的就会返回-1。

1
2
3
4
StringBuffer s1 = new StringBuffer().append("bbbaaa");
System.out.println(s1.indexOf("ba")); //2
System.out.println(s1.indexOf("ba",2)); //2
System.out.println(s1.indexOf("mn")); //-1

lastIndexOf()

和indexOf()的用法一样,只不过是从后往前匹配,也支持从指定索引开始从后往前去匹配。

1
2
3
4
StringBuffer s1 = new StringBuffer().append("bbb");
s1.append("aaa");
System.out.println(s1.toString()); //bbbaaa
System.out.println(s1.lastIndexOf("b",5)); //2

reverse()

1
2
3
StringBuffer s1 = new StringBuffer().append("bbbaaa");
System.out.println(s1.reverse());
System.out.println(s1.toString()); ///aaabbb

length()

返回长度。

HashMap

概述

在JDK1.6,JDK1.7中,HashMap采用位桶+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,HashMap采用位桶+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。参考链接

HashMap的实现是不同步的,这意味着它不是线程安全的。它的key,value都可以是null。此外,HashMap中的映射不是有序的。

HashMap的实例有两个参数影响其性能:“初始容量”和“加载因子”。初始容量是哈希表在创建时的容量。加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。通常默认加载因子是0.75,这是在时间和空间成本上寻求一种折衷。

使用

构造函数

1
2
3
4
5
6
7
8
// 默认构造函数。
HashMap()
// 指定“容量大小”的构造函数
HashMap(int capacity)
// 指定“容量大小”和“加载因子”的构造函数
HashMap(int capacity, float loadFactor)
// 包含“子Map”的构造函数
HashMap(Map<? extends K, ? extends V> map)

API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void                 clear()	//清空HashMap
Object clone() //克隆一个HashMap对象并返回
boolean containsKey(Object key) //判断HashMap是否包含key
boolean containsValue(Object value) //判断HashMap是否包含“值为value”的元素
Set<Entry<K, V>> entrySet() //返回“HashMap中所有Entry的集合”,它是一个集合
V get(Object key) //获取key对应的value
V getOrDefault(Object key, V value) //JDK1.8
boolean isEmpty()
Set<K> keySet()
V put(K key, V value) //对外提供接口,让HashMap对象可以通过put()将“key-value”添加到HashMap中
void putAll(Map<? extends K, ? extends V> map) //将"m"的全部元素都添加到HashMap中
V remove(Object key) //删除“键为key”元素
int size() //包含键值对的数量
Collection<V> values()
V replace(key,val) //对于存在的key,调用replace方法,会替换原来的value,并返回旧value,这和put的效果是一样的;对于不存在的key,replace方法什么都不做。

遍历

  1. 遍历HashMap的键值对

    第一步:根据entrySet()获取HashMap的“键值对”的Set集合,效率高。
    第二步:通过Iterator迭代器遍历“第一步”得到的集合。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // map中的key是String类型,value是Integer类型
    Integer integ = null;
    Iterator iter = map.entrySet().iterator();
    while(iter.hasNext()) {
    Map.Entry entry = (Map.Entry)iter.next();
    // 获取key
    key = (String)entry.getKey();
    // 获取value
    integ = (Integer)entry.getValue();
    }
  2. 遍历HashMap的键

    第一步:根据keySet()获取HashMap的“键”的Set集合。
    第二步:通过Iterator迭代器遍历“第一步”得到的集合。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 假设map是HashMap对象
    // map中的key是String类型,value是Integer类型
    String key = null;
    Integer integ = null;
    Iterator iter = map.keySet().iterator();
    while (iter.hasNext()) {
    // 获取key
    key = (String)iter.next();
    // 根据key,获取value
    integ = (Integer)map.get(key);
    }
  3. 遍历HashMap的值

    第一步:根据value()获取HashMap的“值”的集合。
    第二步:通过Iterator迭代器遍历“第一步”得到的集合。

    1
    2
    3
    4
    5
    6
    7
    // map中的key是String类型,value是Integer类型
    Integer value = null;
    Collection c = map.values();
    Iterator iter= c.iterator();
    while (iter.hasNext()) {
    value = (Integer)iter.next();
    }

String

1
2
3
4
5
6
7
String x="fmn";
x.toUpperCase();
String y=x.replace('f','F');
String x="fmn"; “fmn”是在常量池里的不可变对象。
x.toUpperCase(); 在堆中new一个"FMN"对象,但无任何引用指向它。
String y=x.replace('f','F'); 在堆中 new一个"Fmn"对象,y指向它。
y=y+"wxy"; 在堆中 重新new一个"Fmnwxy"对象, 修改y指向,现在y指向它。

substring()