本文目录导读:

我来提供一个完整的Java短链接生成案例,包含多种实现方案。
基于哈希+Base62编码(最常用)
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class ShortUrlGenerator {
// 62个字符的字符表
private static final String BASE62_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static final int BASE = 62;
// 存储在内存中的映射(实际应用中用数据库/Redis)
private static Map<String, String> shortToLongMap = new HashMap<>();
private static Map<String, String> longToShortMap = new HashMap<>();
/**
* 方法1:基于MD5哈希生成短链接
*/
public static String generateByMD5(String longUrl, String salt) {
try {
// 添加随机盐值避免碰撞
String input = longUrl + salt;
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] digest = md5.digest(input.getBytes());
// 取前8个字节
StringBuilder hexString = new StringBuilder();
for (int i = 0; i < 8; i++) {
hexString.append(String.format("%02x", digest[i]));
}
// 将16进制转成62进制
String hexStringStr = hexString.toString();
StringBuilder shortUrl = new StringBuilder();
for (int i = 0; i < 6; i++) { // 生成6位短码
shortUrl.append(hexToChar(hexStringStr.substring(i * 2, i * 2 + 2)));
}
return shortUrl.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5 algorithm not found", e);
}
}
/**
* 方法2:基于UUID生成
*/
public static String generateByUUID() {
return UUID.randomUUID().toString().replace("-", "")
.substring(0, 8) // 取8位
.toUpperCase();
}
/**
* 方法3:基于自增ID+Base62编码
*/
private static long counter = 1000000000L; // 模拟自增ID
public static synchronized String generateByIncrement() {
return convertToBase62(counter++);
}
/**
* 方法4:基于32位整数+Base62编码
*/
public static String generateByRandom() {
int randomInt = Math.abs(UUID.randomUUID().hashCode());
return convertToBase62(randomInt);
}
/**
* 将10进制转换为62进制
*/
private static String convertToBase62(long number) {
if (number == 0) {
return "0";
}
StringBuilder result = new StringBuilder();
while (number > 0) {
int remainder = (int) (number % BASE);
result.insert(0, BASE62_CHARS.charAt(remainder));
number /= BASE;
}
return result.toString();
}
/**
* 将16进制字符串转换为字符
*/
private static char hexToChar(String hex) {
int num = Integer.parseInt(hex, 16);
return BASE62_CHARS.charAt(num % BASE);
}
/**
* 保存映射关系
*/
public static void saveMapping(String shortCode, String longUrl) {
shortToLongMap.put(shortCode, longUrl);
longToShortMap.put(longUrl, shortCode);
}
/**
* 根据短码获取长URL
*/
public static String getLongUrl(String shortCode) {
return shortToLongMap.get(shortCode);
}
public static void main(String[] args) {
String longUrl = "https://www.example.com/blog/article/123456789?page=1&size=10";
// 测试各种生成方式
System.out.println("原始URL: " + longUrl);
System.out.println("长度: " + longUrl.length());
// 1. MD5方式
String md5ShortCode = generateByMD5(longUrl, "salt123");
System.out.println("\nMD5方式生成: " + md5ShortCode);
System.out.println("长度: " + md5ShortCode.length());
saveMapping(md5ShortCode, longUrl);
// 2. UUID方式
String uuidShortCode = generateByUUID();
System.out.println("\nUUID方式生成: " + uuidShortCode);
System.out.println("长度: " + uuidShortCode.length());
saveMapping(uuidShortCode, longUrl);
// 3. 自增ID方式
String incrementShortCode = generateByIncrement();
System.out.println("\n自增ID方式生成: " + incrementShortCode);
System.out.println("长度: " + incrementShortCode.length());
saveMapping(incrementShortCode, longUrl);
// 4. 随机数方式
String randomShortCode = generateByRandom();
System.out.println("\n随机数方式生成: " + randomShortCode);
System.out.println("长度: " + randomShortCode.length());
saveMapping(randomShortCode, longUrl);
// 5. 完整的短链接
String domain = "https://short.link/";
System.out.println("\n完整短链接示例: " + domain + md5ShortCode);
// 6. 验证反向解析
System.out.println("\n反向解析测试:");
System.out.println("短码 " + md5ShortCode + " -> " + getLongUrl(md5ShortCode));
// 7. 批量生成测试
System.out.println("\n批量生成测试:");
for (int i = 0; i < 10; i++) {
String code = generateByIncrement();
System.out.println("生成短码 " + (i + 1) + ": " + code);
}
}
}
完整的短链接服务类
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
/**
* 完整的短链接服务类
*/
public class ShortUrlService {
// 基础配置
private static final String DOMAIN = "https://short.url/";
private static final int SHORT_CODE_LENGTH = 8;
private static final long EXPIRATION_TIME = 30L * 24 * 60 * 60 * 1000; // 30天有效
// 字符表
private static final String ALPHABET = "BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789";
private static final int BASE = ALPHABET.length();
// 存储结构(实际应用使用Redis/数据库)
private static final Map<String, UrlEntry> urlStore = new HashMap<>();
// 防止重复生成的缓存
private static final Map<String, String> longUrlCache = new HashMap<>();
// URL实体类
public static class UrlEntry {
private final String longUrl;
private final long createdAt;
private final long expiresAt;
private long accessCount;
public UrlEntry(String longUrl) {
this.longUrl = longUrl;
this.createdAt = System.currentTimeMillis();
this.expiresAt = this.createdAt + EXPIRATION_TIME;
this.accessCount = 0;
}
public String getLongUrl() {
return longUrl;
}
public long getCreatedAt() {
return createdAt;
}
public long getExpiresAt() {
return expiresAt;
}
public void incrementAccessCount() {
this.accessCount++;
}
public long getAccessCount() {
return accessCount;
}
public boolean isExpired() {
return System.currentTimeMillis() > expiresAt;
}
@Override
public String toString() {
return String.format("UrlEntry{longUrl='%s', createdAt=%s, expiresAt=%s, accessCount=%d}",
longUrl, new Date(createdAt), new Date(expiresAt), accessCount);
}
}
/**
* 生成短链接(完整流程)
*/
public String createShortUrl(String longUrl) {
// 检查是否已存在
String existingCode = longUrlCache.get(longUrl);
if (existingCode != null) {
UrlEntry entry = urlStore.get(existingCode);
if (entry != null && !entry.isExpired()) {
return DOMAIN + existingCode;
}
}
// 生成唯一短码
String shortCode;
int attempts = 0;
do {
shortCode = generateUniqueCode();
attempts++;
} while (urlStore.containsKey(shortCode) && attempts < 10);
// 保存映射
UrlEntry entry = new UrlEntry(longUrl);
urlStore.put(shortCode, entry);
longUrlCache.put(longUrl, shortCode);
return DOMAIN + shortCode;
}
/**
* 生成唯一短码
*/
private String generateUniqueCode() {
// 方法1:基于时间戳
long timestamp = System.nanoTime();
String timeBase62 = base62Encode(timestamp);
// 方法2:加入随机数
int random = ThreadLocalRandom.current().nextInt(0, BASE * BASE);
String randomBase62 = base62Encode(random);
// 组合并调整长度
String combined = timeBase62 + randomBase62;
// 如果太长,取前N位
if (combined.length() > SHORT_CODE_LENGTH) {
combined = combined.substring(0, SHORT_CODE_LENGTH);
}
// 如果太短,填充
while (combined.length() < SHORT_CODE_LENGTH) {
combined += base62Encode(ThreadLocalRandom.current().nextInt(BASE));
}
return combined;
}
/**
* Base62编码
*/
private String base62Encode(long number) {
if (number == 0) {
return String.valueOf(ALPHABET.charAt(0));
}
StringBuilder sb = new StringBuilder();
while (number > 0) {
int mod = (int) (number % BASE);
sb.append(ALPHABET.charAt(mod));
number /= BASE;
}
return sb.reverse().toString();
}
/**
* 获取原始URL
*/
public String getOriginalUrl(String shortCode) {
if (shortCode != null && shortCode.startsWith(DOMAIN)) {
shortCode = shortCode.substring(DOMAIN.length());
}
UrlEntry entry = urlStore.get(shortCode);
if (entry == null) {
return null;
}
if (entry.isExpired()) {
urlStore.remove(shortCode);
longUrlCache.remove(entry.getLongUrl());
return null;
}
entry.incrementAccessCount();
return entry.getLongUrl();
}
/**
* 删除短链接
*/
public boolean deleteShortUrl(String shortCode) {
if (shortCode != null && shortCode.startsWith(DOMAIN)) {
shortCode = shortCode.substring(DOMAIN.length());
}
UrlEntry entry = urlStore.remove(shortCode);
if (entry != null) {
longUrlCache.remove(entry.getLongUrl());
return true;
}
return false;
}
/**
* 获取统计数据
*/
public UrlEntry getStats(String shortCode) {
if (shortCode != null && shortCode.startsWith(DOMAIN)) {
shortCode = shortCode.substring(DOMAIN.length());
}
return urlStore.get(shortCode);
}
/**
* 清理过期链接
*/
public int cleanExpiredUrls() {
int count = 0;
for (Map.Entry<String, UrlEntry> entry : urlStore.entrySet()) {
if (entry.getValue().isExpired()) {
urlStore.remove(entry.getKey());
longUrlCache.remove(entry.getValue().getLongUrl());
count++;
}
}
return count;
}
/**
* 生成二维码(如果需要的功能)
*/
public void generateQRCode(String shortUrl) {
// 使用zxing库等生成二维码
// 这里只是示例,实际实现需要添加依赖
System.out.println("生成二维码: " + shortUrl);
}
// 测试主方法
public static void main(String[] args) {
ShortUrlService service = new ShortUrlService();
// 测试用例
String[] urls = {
"https://www.google.com/search?q=java+short+url",
"https://stackoverflow.com/questions/12345678",
"https://github.com/username/repository",
"https://www.baidu.com/s?wd=短链接",
"https://example.com/product/12345?ref=promo&utm_source=test"
};
System.out.println("=== 短链接生成测试 ===\n");
for (String url : urls) {
String shortUrl = service.createShortUrl(url);
System.out.println("原始URL: " + url);
System.out.println("短链接: " + shortUrl);
// 解析测试
String code = shortUrl.replace(DOMAIN, "");
String original = service.getOriginalUrl(code);
System.out.println("解析URL: " + original);
System.out.println("解析成功: " + Objects.equals(url, original));
// 测试重复创建
String shortUrl2 = service.createShortUrl(url);
System.out.println("重复创建: " + shortUrl2);
System.out.println("是否相同: " + shortUrl.equals(shortUrl2));
System.out.println("------------------------\n");
}
// 批量生成测试
System.out.println("=== 并发批量生成测试 ===");
service.testConcurrentCalls();
// 统计信息
System.out.println("\n=== 统计信息 ===");
for (String url : urls) {
String shortUrl = service.createShortUrl(url);
String code = shortUrl.replace(DOMAIN, "");
UrlEntry stats = service.getStats(code);
if (stats != null) {
System.out.println("URL: " + stats.getLongUrl());
System.out.println("创建时间: " + new Date(stats.getCreatedAt()));
System.out.println("过期时间: " + new Date(stats.getExpiresAt()));
System.out.println("访问次数: " + stats.getAccessCount());
System.out.println("------------------------");
}
}
}
/**
* 并发测试
*/
private void testConcurrentCalls() {
Runnable task = () -> {
for (int i = 0; i < 10; i++) {
String url = "https://example.com/concurrent/" +
Thread.currentThread().getName() + "/" + i;
String shortUrl = createShortUrl(url);
System.out.println("线程 " + Thread.currentThread().getName() +
" 生成了: " + shortUrl);
}
};
Thread[] threads = new Thread[5];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(task, "Thread-" + (i + 1));
threads[i].start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
使用外部库(Guava)
import com.google.common.hash.Hashing;
import java.nio.charset.StandardCharsets;
public class GuavaShortUrlGenerator {
private static final String DOMAIN = "https://short.url/";
private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static final int BASE = ALPHABET.length();
/**
* 使用Guava的MurmurHash算法
*/
public static String generateShortCode(String longUrl) {
// 计算哈希值
long hash = Hashing.murmur3_32_fixed() // 或使用 murmur3_128
.hashString(longUrl, StandardCharsets.UTF_8)
.asInt();
// 转换为正数
long positiveHash = hash & Long.MAX_VALUE;
// Base62编码
StringBuilder shortCode = new StringBuilder();
while (positiveHash > 0) {
int index = (int) (positiveHash % BASE);
shortCode.insert(0, ALPHABET.charAt(index));
positiveHash /= BASE;
}
// 确保至少6位
while (shortCode.length() < 6) {
shortCode.insert(0, ALPHABET.charAt(0));
}
return shortCode.toString();
}
public static void main(String[] args) {
String[] urls = {
"https://www.google.com/search?q=java",
"https://stackoverflow.com/questions",
"https://github.com/username"
};
for (String url : urls) {
String code = generateShortCode(url);
System.out.println("URL: " + url);
System.out.println("短码: " + code);
System.out.println("短链接: " + DOMAIN + code);
System.out.println("长度: " + code.length());
System.out.println("------------------------");
}
}
}
使用说明
添加依赖(如果使用方案三)
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>
生产环境建议
- 使用Redis存储映射关系
- 考虑使用分布式ID生成器(如Snowflake)
- 添加缓存层(如Caffeine)
- 实现数据库持久化
- 设置过期时间
- 添加访问统计
性能优化建议
- 预生成ID池
- 使用连接池
- 批量操作
- 异步处理
这个案例提供了多种生成方式和完整的服务实现,可以根据实际需求选择使用。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播