Cada octeto são 8 bits. Um int tem 4x8 bits. Logo, um endereço ip inteiro corresponde a um int.
Com isso, para gerar um ip atrás do outro, basta um simples for:
import java.util.Set;
import java.util.TreeSet;
public class IP implements Comparable<IP> {
private int address;
public IP(int oct1, int oct2, int oct3, int oct4) {
address = (oct1 & 0xFF) << 24;
address += (oct2 & 0xFF) << 16;
address += (oct3 & 0xFF) << 8;
address += (oct4 & 0xFF);
}
private IP(int ip)
{
this.address = ip;
}
public int getOct1() {
return (address >> 24) & 0xFF;
}
public int getOct2() {
return (address >> 16) & 0xFF;
}
public int getOct3() {
return (address >> 8) & 0xFF;
}
public int getOct4() {
return address & 0xFF;
}
@Override
public String toString() {
return getOct1() + "." + getOct2() + "." + getOct3() + "." + getOct4();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final IP other = (IP) obj;
if (this.address != other.address) {
return false;
}
return true;
}
@Override
public int hashCode() {
return address;
}
@Override
public int compareTo(IP o) {
return getAddress() == o.getAddress() ? 0 :
(getAddress() > o.getAddress() ? 1 : -1);
}
private long getAddress()
{
return (long)(address & 0xFFFFFFFFL);
}
public static Set<IP> todosOsIps(IP min, IP max)
{
Set<IP> ips = new TreeSet<IP>();
for (long i = min.getAddress(); i < max.getAddress(); i++)
ips.add(new IP((int)i));
return ips;
}
public static Set<IP> todosOsIps()
{
Set<IP> ips = new TreeSet<IP>();
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++)
ips.add(new IP(i));
return ips;
}
}
public class Main {
public static void main(String[] args) {
IP ip1 = new IP( 200, 200, 200, 250 );
IP ip2 = new IP( 200, 200, 200, 255 );
for (IP ip : IP.todosOsIps(ip1, ip2))
System.out.println(ip);
}
}
Entretanto, todos os IPs possíveis correspondem a 4.294.967.296 endereços.
No caso do Java, somos obrigados a converter ints para longs na hora de fazer o for pois não temos tipos unsigned. Em C++ isso não seria necessário.
É importante também entender os IPs como ints pq fica facílimo entender como aplicar máscaras de sub-rede. Para aplicar a mascara 255.255.255.0 (0xFFFFFF00) para um IP qualquer e obter o endereço da máquina na rede, basta fazer uma operação de AND. Se quiser obter o endereço da sub-rede, use um OR. Isso também explica aquelas máscaras que tem um número diferente de 0 no final.
Quanto ao Hastable. Ele geralmente associa uma chave a um valor. Não entendi porque usar nessa situação também. Outra coisa, o tipo HashTable já é desaconselhado desde o Java 1.2. Se for usar uma estrutura de hash, use a classe hashMap:
Map<Integer, String> mapa = new HashMap<Integer, String>();
Se quiser o mapa ordenado pela chave, use o TreeMap:
Map<Integer, String> mapa = new TreeMap<Integer, String>();
Nesse caso, a chave deve ser comparable, ou você deve fornecer um comparator para o mapa.