I've just started learning java since last week. I'm using book called 'head first java' and i'm struggling with solving problems about ArrayList. Error says "The method setLocationCells(ArrayList) in the type DotCom is not applicable for the
arguments (int[])" and I haven't found the solution :( help me..!
enter image description here
This looks like a Locate & Conquer type game similar to the game named Battleship with the exception that this game is a single player game played with a single hidden ship in a single horizontal row of columnar characters. Rather simplistic but kind of fun to play I suppose. The hard part is to locate the hidden ship but once you've located it, conquering (sinking) it becomes relatively easy. I'm sure this isn't the games' intent since it is after all named "The Dot Com Game" but the analogy could be possibly helpful.
There are several issues with your code but there are two major ones that just can not be there for the game to work:
Issue #1: The call to the DotCom.setLocationCells() method:
The initial problem is located within the DotComGame class on code line 13 (as the Exception indicates) where the call is made to the DotCom.setLocationCells() method. As already mentioned in comments the wrong parameter type is passed to this method. You can not pass an int[] Array to the setLocationCell() method when this method contains a parameter signature that stipulates it requires an ArrayList object. The best solution in my opinion would be to satisfy the setLocationCells() method parameter requirement...supply an ArrayList to this method.
The reason I say this is because all methods within the DotCom class work with an established ArrayList and one of the tasks of one of these methods (the checkYourself() method) actually removes elements from the ArrayList which is easy to do from a collection but very cumbersome to do the same from an Array.
To fix this problem you will need to change the data type for the locations variable located within the DotComGame class. Instead of using:
int[] locations = {randomNum, randomNum + 1, randomNum + 2};
you should have:
ArrayList<Integer> locations = new ArrayList<>(
Arrays.asList(random, randomNum + 1, randomNum + 2));
or you could do it this way:
ArrayList<Integer> locations = new ArrayList<>();
locations.add(randomNum);
locations.add(randomNum + 1);
locations.add(randomNum + 2);
There are other ways but these will do for now. Now, when the call to the setLocationCells() method is made you ahouldn't get an exception this issue should now be resolved.
Issue #2: The call to the DotCom.checkYourself() method:
Again, this particular issue is located within the DotComGame class on code line 18 where the call is made to the DotCom.checkYourself() method. Yet another parameter data type mismatch. You are trying to pass a variable of type String (named guess) to this method whereas its signature stipulates that it requires an integer (int) value. That again is a no go.
To fix this problem you will need to convert the string numerical value held by the guess variable to an Integer (int) value. So instead of having this:
while(isAlive) {
String guess = helper.getUserInput("Enter a Number: ");
String result = theDotCom.checkYourself(guess);
// ... The rest of your while loop code ...
}
you should have something like:
while(isAlive) {
String guess = helper.getUserInput("Enter a Number: ");
/* Validate. Ensure guess holds a string representation
of a Integer numerical value. */
if (!guess.matches("\\d+")) {
System.err.println("Invalid Value (" + guess
+ ") Supplied! Try again...");
continue;
}
int guessNum = Integer.parseInt(guess);
String result = theDotCom.checkYourself(guessNum);
numOfGuesses++;
if (result.equals("kill")) {
isAlive = false;
System.out.println(numOfGuesses + " guesses!");
}
else if (result.equals("hit")) {
// Do Something If You Like
System.out.println("HIT!");
}
else {
System.out.println("Missed!");
}
}
Below is a game named Simple Battleship which I based off of your code images (please don't use images for code anymore - I hate using online OCR's ;)
BattleshipGame.java - The application start class:
import java.awt.Toolkit;
public class BattleshipGame {
public static int gameLineLength = 10;
public static void main(String[] args) {
GameHelper helper = new GameHelper();
Battleship theDotCom = new Battleship();
int score = 0; // For keeping an overall score
// Display About the game...
System.out.println("Simple Battleship Game");
System.out.println("======================");
System.out.println("In this game you will be displayed a line of dashes.");
System.out.println("Each dash has the potential to hide a section of a");
System.out.println("hidden Battleship. The size of this ship is randomly");
System.out.println("chosen by the game engine and can be from 1 to 5 sections");
System.out.println("(characters) in length. The score for each battle is based");
System.out.println("on the length of the game line that will be displayed to");
System.out.println("you (default is a minimum of 10 charaters). You now have");
System.out.println("the option to supply the game line length you want to play");
System.out.println("with. If you want to use the default then just hit ENTER:");
System.out.println();
// Get the desire game line length
String length = helper.getUserInput("Desired Game Line Length: --> ", "Integer", true, 10, 10000);
if (!length.isEmpty()) {
gameLineLength = Integer.parseInt(length);
}
System.out.println();
// Loop to allow for continuous play...
boolean alwaysReplay = true;
while (alwaysReplay) {
int numOfGuesses = 0;
/* Create a random ship size to hide within the line.
It could be a size from 1 to 5 characters in length. */
int shipSize = new java.util.Random().nextInt((5 - 1) + 1) + 1;
int randomNum = (int) (Math.random() * (gameLineLength - (shipSize - 1)));
int[] locations = new int[shipSize];
for (int i = 0; i < locations.length; i++) {
locations[i] = randomNum + i;
}
System.out.println("Destroy the " + shipSize + " character ship hidden in the");
System.out.println("displayed line below:");
System.out.println();
String gameLine = String.join("", java.util.Collections.nCopies(gameLineLength, "-"));
theDotCom.setLocationCells(locations);
// Play current round...
boolean isAlive = true;
while (isAlive == true) {
System.out.println(gameLine);
String guess = helper.getUserInput("Enter a number from 1 to " + gameLineLength
+ " (0 to quit): --> ", "Integer", 1, gameLineLength);
int idx = Integer.parseInt(guess);
if (idx == 0) {
System.out.println("Quiting with an overall score of: " + score + " ... Bye-Bye");
alwaysReplay = false;
break;
}
idx = idx - 1;
String result = theDotCom.checkYourself(idx);
numOfGuesses++;
System.out.println(result);
if (result.equalsIgnoreCase("kill")) {
Toolkit.getDefaultToolkit().beep();
isAlive = false;
/* Tally the score dependent upon the gameLineLength... */
if (gameLineLength <= 10) { score += 5; }
else if (gameLineLength > 10 && gameLineLength <= 20) { score += 10; }
else if (gameLineLength > 20 && gameLineLength <= 30) { score += 15; }
else if (gameLineLength > 30 && gameLineLength <= 40) { score += 20; }
else { score += 25; }
gameLine = gameLine.substring(0, idx) + "x" + gameLine.substring(idx + 1);
System.out.println(gameLine);
System.out.println(numOfGuesses + " guesses were made to sink the hidden ship.");
System.out.println("Your overall score is: " + (score < 0 ? 0 : score));
}
else if (result.equalsIgnoreCase("hit")) {
gameLine = gameLine.substring(0, idx) + "x" + gameLine.substring(idx + 1);
}
if (result.equalsIgnoreCase("miss")) {
score -= 1;
}
System.out.println();
}
// Play Again? [but only if 'alwaysReplay' holds true]
if (alwaysReplay) {
String res = helper.getAnything("<< Press ENTER to play again >>\n"
+ "<< or enter 'q' to quit >>");
if (res.equalsIgnoreCase("q")) {
System.out.println("Quiting with an overall score of: " + score + " ... Bye-Bye");
break;
}
System.out.println();
}
}
}
}
GameHelper.java - The GameHelper class:
import java.util.Scanner;
public class GameHelper {
private final Scanner in = new Scanner(System.in);
public String getUserInput(String prompt, String responseType, int... minMAX) {
int min = 0, max = 0;
if (minMAX.length == 2) {
min = minMAX[0];
max = minMAX[1];
}
if (minMAX.length > 0 && min < 1 || max < 1) {
throw new IllegalArgumentException("\n\ngetUserInput() Method Error! "
+ "The optional parameters 'min' and or 'max' can not be 0!\n\n");
}
String response = "";
while (response.isEmpty()) {
if (prompt.trim().endsWith("-->")) {
System.out.print(prompt);
}
else {
System.out.println(prompt);
}
response = in.nextLine().trim();
if (responseType.matches("(?i)\\b(int|integer|float|double)\\b")) {
if (!response.matches("-?\\d+(\\.\\d+)?") ||
(responseType.toLowerCase().startsWith("int") && response.contains("."))) {
System.err.println("Invalid Entry (" + response + ")! Try again...");
response = "";
continue;
}
}
// Check entry range value if the entry is to be an Integer
if (responseType.toLowerCase().startsWith("int")) {
int i = Integer.parseInt(response);
if (i != 0 && (i < min || i > max)) {
System.err.println("Invalid Entry (" + response + ")! Try again...");
response = "";
}
}
}
return response;
}
public String getUserInput(String prompt, String responseType, boolean allowNothing, int... minMAX) {
int min = 0, max = 0;
if (minMAX.length == 2) {
min = minMAX[0];
max = minMAX[1];
}
if (minMAX.length > 0 && min < 1 || max < 1) {
throw new IllegalArgumentException("\n\ngetUserInput() Method Error! "
+ "The optional parameters 'min' and or 'max' can not be 0!\n\n");
}
String response = "";
while (response.isEmpty()) {
if (prompt.trim().endsWith("-->")) {
System.out.print(prompt);
}
else {
System.out.println(prompt);
}
response = in.nextLine().trim();
if (response.isEmpty() && allowNothing) {
return "";
}
if (responseType.matches("(?i)\\b(int|integer|float|double)\\b")) {
if (!response.matches("-?\\d+(\\.\\d+)?") ||
(responseType.toLowerCase().startsWith("int") && response.contains("."))) {
System.err.println("Invalid Entry (" + response + ")! Try again...");
response = "";
continue;
}
}
// Check entry range value if the entry is to be an Integer
if (responseType.toLowerCase().startsWith("int")) {
int i = Integer.parseInt(response);
if (i != 0 && (i < min || i > max)) {
System.err.println("Invalid Entry (" + response + ")! Try again...");
response = "";
}
}
}
return response;
}
public String getAnything(String prompt) {
if (prompt.trim().endsWith("-->")) {
System.out.print(prompt);
}
else {
System.out.println(prompt);
}
return in.nextLine().trim();
}
}
Battleship.java - The Battleship class:
import java.util.ArrayList;
public class Battleship {
private ArrayList<Integer> locationCells;
public void setLocationCells(java.util.ArrayList<Integer> loc) {
locationCells = loc;
}
// Overload Method (Java8+)
public void setLocationCells(int[] loc) {
locationCells = java.util.stream.IntStream.of(loc)
.boxed()
.collect(java.util.stream.Collectors
.toCollection(java.util.ArrayList::new));
}
/*
// Overload Method (Before Java8)
public void setLocationCells(int[] loc) {
// Clear the ArrayList in case it was previously loaded.
locationCells.clear();
// Fill the ArrayList with integer elements from the loc int[] Array
for (int i = 0; i < loc.length; i++) {
locationCells.add(loc[i]);
}
}
*/
/**
* Completely removes one supplied Integer value from all elements
* within the supplied Integer Array if it exist.<br><br>
*
* <b>Example Usage:</b><pre>
*
* {#code int[] a = {103, 104, 100, 10023, 10, 140, 2065};
* a = removeFromArray(a, 104);
* System.out.println(Arrays.toString(a);
*
* // Output will be: [103, 100, 10023, 10, 140, 2065]}</pre>
*
* #param srcArray (Integer Array) The Integer Array to remove elemental
* Integers from.<br>
*
* #param intToDelete (int) The Integer to remove from elements within the
* supplied Integer Array.<br>
*
* #return A Integer Array with the desired elemental Integers removed.
*/
public static int[] removeFromArray(int[] srcArray, int intToDelete) {
int[] arr = {};
int cnt = 0;
boolean deleteIt = false;
for (int i = 0; i < srcArray.length; i++) {
if (srcArray[i] != intToDelete) {
arr[cnt] = srcArray[i];
cnt++;
}
}
return arr;
}
public String checkYourself(int userInput) {
String result = "MISS";
int index = locationCells.indexOf(userInput);
if (index >= 0) {
locationCells.remove(index);
if (locationCells.isEmpty()) {
result = "KILL";
}
else {
result = "HIT";
}
}
return result;
}
}
I made this code for extracting Polynomial coefficients and also evaluating equation in a point,and it is work.
but i want to modify that so the user can enter any shape of polynomial equation.
in my code you have to enter equation like this:
2*x^2+3*x^1+4
but i want :
2*x^5+1*x+6
also if there any term with same power , their coeffs must be added together.
Here is my code in java:
package Priest;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class Equation {
private String Eq;
private final String[] C;
private int Deg;
private final String EqHolder;
public Equation(String Equation) {
this.Eq = Equation;
EqHolder = Equation;
Eq = Eq.replaceAll("[^0-9\\-\\.]+", " ");
Eq = Eq.replaceAll("-", " -");
this.C = Eq.split(" ");
}
public String SourceEquation() {
return EqHolder.toUpperCase().replaceAll("\\*", "").replaceAll("[a-zA-Z]", "\\*(X)").replaceAll("\\+", "\\ + ").replaceAll("\\-", "\\ - ");
}
public List<BigDecimal> CaptureCoeff() {
getDegree();
List<BigDecimal> Temp = new ArrayList<>();
for (String S : C) {
Temp.add(new BigDecimal(S));
}
int Location = Temp.indexOf(BigDecimal.valueOf(Deg));
List<BigDecimal> Coeffs = new ArrayList<>();
for (int Counter = Location - 1; Counter < Temp.size(); Counter += 2) {
Coeffs.add(Temp.get(Counter));
}
return Coeffs;
}
public int getDegree() {
int Degree = 0;
for (int Counter = 0; Counter < C.length; Counter += 2) {
if ((new Double(C[Counter])) != 0) {
Degree = new Integer(C[Counter + 1]);
this.Deg = Degree;
break;
}
}
return Degree;
}
public BigDecimal Evaluate(List<BigDecimal> Coefficients, double EvalPoint) {
BigDecimal Output = BigDecimal.ZERO;
for (int Index = 0; Index < Coefficients.size(); Index++) {
Output = Output.add(Coefficients.get(Index).multiply(BigDecimal.valueOf(EvalPoint).pow(Deg--)));
}
return Output;
}
}
and main class:
package Priest;
import java.math.RoundingMode;
public class MainClass {
public static void main(String[] args) {
long Start = System.nanoTime();
String Str = "3.1415x^5-12.6x^4+6x^3+12*x^2-6*x^1-0";
Equation E = new Equation(Str);
System.out.println("Equation is: " + E.SourceEquation());
System.out.println("Coefficients :" + E.CaptureCoeff());
System.out.println("Polynomial Degree: " + E.getDegree());
double Target = 47.784;
System.out.println("Equation # (X:" + Target + ")= " + E.Evaluate(E.CaptureCoeff(), Target).setScale(15, RoundingMode.HALF_UP));
System.out.println("Elapsed Time: " + String.format("%.20G", (System.nanoTime() - Start) / 1.0e6) + " ms.");
}
}
the output:
run:
Equation is: 3.1415*(X)^5 - 12.6*(X)^4 + 6*(X)^3 + 12*(X)^2 - 6*(X)^1 - 0
Coefficients :[3.1415, -12.6, 6, 12, -6, 0]
Polynomial Degree: 5
Equation # (X:47.784)= 717609084.382589022327914
Elapsed Time: 32.306242000000000000 ms.
BUILD SUCCESSFUL (total time: 0 seconds)
Let's go with the following equation String Str2 = "3.1415x^5+6x^2+12*x-5";
Here is the code that I have added upon your code in order to preprocess this equation and made it compatible to your actual logic so that It will treat it without any major change to your code.
To be totally accurate I had to change the following in your equation class:
public List<BigDecimal> CaptureCoeff() {
getDegree();
List<BigDecimal> Temp = new ArrayList<BigDecimal>();
for (String S : C) {
if (! "".equals(S.trim())) {
Temp.add(new BigDecimal(S));
}
}
So I have added the control to check that none of these S strings is trim - empty.
Here is my preprocessing code.
I have added a method called powerSplitt that allows to splitt the equation on the basis of the '^' char.
Then I created another method called generateNullCoeffPolynomeWithDegree that generate a monome in the form 0*X^k. And a similar one that generate all the similar intermediate monomes between the greater power and the lesser power
Example:
String str3 = generateAllNullCoeffPolynomesWithDegreeExclusiveBetween(5, 2);
System.out.println("all poly = " + str3);
will generate: all poly = 0*x^4+0*x^3
Then I created a buildPreProcessedPolynome that takes the initial equation and pre process it to produce one with the null monomes inside of it. And then I just gave it to your equation program and it could process it fine!!!
Here is the code and a call example all done in the MainClass
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
public class MainClass {
private static List<String> workList = new ArrayList<String>();
public static void powerSplitt(String equationText) {
char[] charsList = equationText.toCharArray();
boolean foundTargetChar = false;
int index = 0;
for (int i = 0; i < charsList.length; i++) {
index = i;
if (charsList[i] == '^') {
foundTargetChar = true;
break;
}
}
if (foundTargetChar) {
workList.add(equationText.substring(0, index));
if (index +1 < equationText.length()) {
powerSplitt(equationText.substring(index+1));
} else {
workList.add(equationText);
return;
}
} else {
workList.add(equationText);
}
}
public static String generateNullCoeffPolynomeWithDegree(int degree) {
return "0*x^" + degree;
}
public static String generateAllNullCoeffPolynomesWithDegreeExclusiveBetween(int startDegree, int endDegree) {
if (startDegree-endDegree <= 1) {
return "";
}
int index = 0;
StringBuilder builder = new StringBuilder();
for (int i = startDegree -1; i > endDegree; i--) {
if (index > 0) {
builder.append("+");
}
builder.append(generateNullCoeffPolynomeWithDegree(i));
index++;
}
return builder.toString();
}
public static String buildPreProcessedPolynome(String initialEquationText) {
workList.clear();
powerSplitt(initialEquationText);
StringBuilder resultBuilder = new StringBuilder();
assert workList.size() >= 3;
resultBuilder.append(workList.get(0));
for (int i = 1; i <= workList.size()-2; i++) {
int actualPower = Integer.parseInt( workList.get(i).substring(0,1));
int nextFoundPower = Integer.parseInt( workList.get(i+1).substring(0,1));
System.out.print("actual power = " + actualPower + " and next power = " + nextFoundPower);
System.out.println();
String additionalPolyParts = generateAllNullCoeffPolynomesWithDegreeExclusiveBetween(actualPower, nextFoundPower);
resultBuilder.append("^" + actualPower);
resultBuilder.append("+");
resultBuilder.append(additionalPolyParts);
resultBuilder.append(workList.get(i).substring(1));
}
resultBuilder.append("^" + workList.get(workList.size()-1));
return resultBuilder.toString();
}
public static void main(String[] args) {
workList.clear();
String Str2 = "3.1415x^5+6x^2+12*x-5";
powerSplitt(Str2);
for (String part: workList) {
System.out.println("PART:" + part);
}
System.out.println("-----------------");
long Start = System.nanoTime();
String str3 = generateAllNullCoeffPolynomesWithDegreeExclusiveBetween(5, 2);
System.out.println("all poly = " + str3);
String preprocessed = buildPreProcessedPolynome(Str2);
System.out.println("preprocessed = " + preprocessed);
System.out.println();
Equation E = new Equation(preprocessed);
System.out.println("Equation is: " + E.SourceEquation());
System.out.println("Coefficients :" + E.CaptureCoeff());
System.out.println("Polynomial Degree: " + E.getDegree());
double Target = 47.784;
System.out.println("Equation # (X:" + Target + ")= " + E.Evaluate(E.CaptureCoeff(), Target).setScale(15, RoundingMode.HALF_UP));
System.out.println("Elapsed Time: " + String.format("%.20G", (System.nanoTime() - Start) / 1.0e6) + " ms.");
}
}
And here is the produced results (I haved added some System.out.println to check the results of my methods calls. I just noticed I have to take into account the last constant as a monome of type K*X^0, but I will leave that to you):
PART:3.1415x
PART:5+6x
PART:2+12*x-5
all poly = 0*x^4+0*x^3
actual power = 5 and next power = 2
preprocessed = 3.1415x^5+0*x^4+0*x^3+6x^2+12*x-5
Equation is: 3.1415*(X)^5 + 0*(X)^4 + 0*(X)^3 + 6*(X)^2 + 12*(X) - 5
Coefficients :[3.1415, 0, 0, 6, 12]
Polynomial Degree: 5
Equation # (X:47.784)= 782631805.485054892561514
Elapsed Time: 18,441978000000000000 ms.
Is there any method to sort this? Or do I just need to split it and use a loop to compare?
Input
123.4.245.23
104.244.253.29
1.198.3.93
32.183.93.40
104.30.244.2
104.244.4.1
Output
1.198.3.93
32.183.93.40
104.30.244.2
104.244.4.1
104.244.253.29
123.4.245.23
So far I use HashMap to stored my data. I want sort the value by the Ip address in ascending order. Seems TreeMap is better choice?
TLDR
Don't rewrite the wheel like I tried too, use the InetAddress class in a Comparator.
Edit
A more efficient and accurate way to do it is with the InetAddress class mentioned above. Credits to 200_success for code.
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Stream;
public class IPSort {
private static String[] TESTS = {"0:0:0:0:0:0:fff:ffff","::FFFF:222.1.41.90",":8:","::::5:6::8","::::5:6::7","::::5:6::8","123..245.23","1...","..1.","123...23",".1..","123..245.23", "123..245.23", "104.244.253.29", "1.198.3.93", "32.183.93.40", "32.183.93.40", "104.30.244.2", "104.244.4.1","0.0.0.1",":a:","::5:3:4:5:6:78","1::2:3","1::2:3:4","1::5:256.2.3.4","1:1:3000.30.30.30","ae80::217:f2ff:254:7:237:98","::2:3:4:5:6:7","2:3:4:5:6:7","::5:3:4:5:6:7:8","::5:3:4:5:6:7:8:9:0","1::8","1::2:3","1::2:3:4","1::5:256.2.3.4","1:1:3000.30.30.30","ae80::217:f2ff:254.7.237.98","1:2:3:4::5:1.2.3.4","2001:0000:1234:0000:0000:C1C0:ABCD:0876","12345::6:7:8","1::1.2.900.4","fe80::","::ffff:0:0"};
public static class InetAddressComparator implements Comparator<InetAddress> {
#Override
public int compare(InetAddress a, InetAddress b) {
byte[] aOctets = a.getAddress(),
bOctets = b.getAddress();
int len = Math.max(aOctets.length, bOctets.length);
for (int i = 0; i < len; i++) {
byte aOctet = (i >= len - aOctets.length) ?
aOctets[i - (len - aOctets.length)] : 0;
byte bOctet = (i >= len - bOctets.length) ?
bOctets[i - (len - bOctets.length)] : 0;
if (aOctet != bOctet) return (0xff & aOctet) - (0xff & bOctet);
}
return 0;
}
}
public static Optional<InetAddress> toInetAddress(String s) {
try {
return Optional.of(InetAddress.getByName(s));
} catch (UnknownHostException badAddress) {
return Optional.empty();
}
}
public static void main(String[] args) throws Exception {
System.out.println("Valid 32-bit addresses");
Arrays.stream(TESTS)
.map(IPSort::toInetAddress)
.filter(Optional::isPresent)
.map(Optional::get)
.filter((addr) -> addr instanceof Inet4Address)
.map(InetAddress::getHostAddress)
.forEach(System.out::println);
System.out.println("\nValid 128-bit addresses");
Arrays.stream(TESTS)
.map(IPSort::toInetAddress)
.filter(Optional::isPresent)
.map(Optional::get)
.filter((addr) -> addr instanceof Inet6Address)
.map(InetAddress::getHostAddress)
.forEach(System.out::println);
System.out.println("\nInvalid addresses");
Arrays.stream(TESTS)
.filter((s) -> !toInetAddress(s).isPresent())
.forEach(System.out::println);
System.out.println("\nSorted addresses");
Arrays.stream(TESTS)
.map(IPSort::toInetAddress)
.filter(Optional::isPresent)
.map(Optional::get)
.sorted(new InetAddressComparator())
.map(InetAddress::getHostAddress)
.forEach(System.out::println);
}
}
Original Answer
In order to sort IPs you first need to know a bit about them. There are two types of IPs; 32 Bit and 128 Bit.
32 Bit Source
The 32 bit IP is split into 4 groups of numbers between 0 and 255. These groups are seperated via a .
A single group, as shown above, is 8 bits of data. This is the reason the numbers in a group are limited between 0 and 255.
For a 32 bit IP to be formatted correctly it should be int.int.int.int. Even if the int is a 0 it must be shown in the IP address. This is different to a 128 bit IP which may omit 0s. For example ::5: which is the same as 0:0:5:0:0:0:0:0.
128 Bit Source
The 128 bit IP is split into 8 groups of numbers between 0 and FFFF (which is equivalent to 65535). Unlike a 32 bit IPs group, these groups are separated buy a :.
A single group, as show above, is 16 bits of data. This is the reason the numbers in the groups are limited between 0 and FFFF.
To format a 128 bit IP properly there are several rules you have to follow. 0s may be omitted from groups and if the remaining groups all are 0 then the groups may also be omitted. The groups have to be separated by a :. If you are omitting groups the last group which isn't a 0 has to be followed by a :. These rules leave us with a format int:int:int:int:int:int:int:int. An example of 0s and groups being omitted would be 58f:::fff:2:. This is the same as 58f:0:0:fff:2:0:0:0.
Sorting
Once the IPs have been sorted into their respective groups they can be sorted. To sort an IP you need to use a method called weighting. This is because simply adding or multiplying different groups together wouldn't work. For example take the these two IPs; 192.5.48.198 and 198.48.5.192. If you add or multiply the values of the groups together you get the same answer. So there is no way to accurately compare them using addition and multiplication. If you use weighting you get something like this.
32 Bit Weighting
Value of IP = (Group one value * 256^4) + (Group two value * 256^3) +
(Group three value * 256^2) + (Group four value * 256)
128 Bit Weighting
Value of IP = (Group one value * 65536^8) + (Group two value * 65536^7) +
(Group three value * 65536^6) + (Group four value * 65536^5) +
(Group five value * 65536^4) + (Group six value * 65536^3) +
(Group seven value * 65536^2) + (Group eight value * 65536)
The Code in Java
As long as the IP is formatted reasonably correctly this code will separate the two kinds of IP and then sort them.
import java.util.*;
import java.math.*; //For BigInteger
import java.util.regex.*;
import java.lang.*;
public class IPSort
{
String[] tests = {":8:","::::5:6::8","::::5:6::7","::::5:6::8","123..245.23","1...","..1.","123...23",".1..","123..245.23", "123..245.23", "104.244.253.29", "1.198.3.93", "32.183.93.40", "32.183.93.40", "104.30.244.2", "104.244.4.1","0.0.0.1",":a:","::5:3:4:5:6:78","1::2:3","1::2:3:4","1::5:256.2.3.4","1:1:3000.30.30.30","ae80::217:f2ff:254:7:237:98"};
ArrayList<String> bit32 = new ArrayList<String>();
ArrayList<String> bit128 = new ArrayList<String>();
ArrayList<String> cleanBit32 = new ArrayList<String>();
ArrayList<String> cleanBit128 = new ArrayList<String>();
boolean myMatcher32Bit(String s)
{
Pattern patter32Bit = Pattern.compile("^(?=(?:[^.]*\\.){3}[^.]*$)(?=(?:[^:]*:){0}[^:]*$)(?=(?:[^a-zA-Z]*[^a-zA-Z])*$)");
Matcher matcher32Bit = patter32Bit.matcher(s);
return matcher32Bit.find();
}
boolean myMatcher128Bit(String s)
{
Pattern patter128Bit = Pattern.compile("^(?=(?:[^.]*\\.){0}[^.]*$)(?=(?:[^:]*:){1,7}[^:]*$)");
Matcher matcher128Bit = patter128Bit.matcher(s);
return matcher128Bit.find();
}
public void sortIntoRespectiveIPTypes()
{
for(String s: tests)
{
if(myMatcher32Bit(s))
{
bit32.add(s);
}
else if(myMatcher128Bit(s))
{
bit128.add(s);
}
}
System.out.println("32 bit IPs");
for(String ip: bit32)
{
System.out.println(" "+ip);
}
System.out.println("\n128 bit IPs");
for(String ip: bit128)
{
System.out.println(" "+ip);
}
int count = 0;
for(String ip: tests)
{
if(myMatcher32Bit(ip)==false && myMatcher128Bit(ip)==false)
{
count++;
}
}
if(count != 0)
{
System.out.println("\nDidn't match an IP format");
for(String ip: tests)
{
if(myMatcher32Bit(ip)==false && myMatcher128Bit(ip)==false)
{
System.out.println(" "+ip);
}
}
}
}
public void sort32BitIPs(ArrayList<String> bit32, ArrayList<String> newBit32)
{
ArrayList<BigInteger> bigInt32Bit = new ArrayList<BigInteger>();
for(String ip:bit32)
{
String[] tempArray = ip.split("\\.");
int i=0;
for(String s:tempArray)
{
if(s.equals(""))
{
tempArray[i]="0";
}
i++;
}
bigInt32Bit.add(convert32Bit(tempArray));
}
Collections.sort(bigInt32Bit);
ArrayList<String> fixFormat = new ArrayList<String>();
for(String ip:bit32)
{
String[] fixArray = ip.split("\\.");
int i=0;
for(String s:fixArray)
{
if(s.equals(""))
{
fixArray[i]="0";
}
i++;
}
StringBuilder strBuilder = new StringBuilder();
for(int i2 = 0; i2 < 4; i2++)
{
if(i2<3)
{
try
{
if(!fixArray[i2].equals(""))
{
strBuilder.append(fixArray[i2]+".");
}
else
{
strBuilder.append(".");
}
}
catch(Exception e)
{
strBuilder.append("0.");
}
}
else
{
try
{
strBuilder.append(fixArray[i2]);
}
catch(Exception e)
{
strBuilder.append("0");
}
}
}
String newString = strBuilder.toString();
fixFormat.add(newString);
bit32=fixFormat;
}
for(BigInteger finalValue:bigInt32Bit)
{
for(String ip:bit32)
{
String[] tempArray = ip.split("\\.");
int i=0;
for(String s:tempArray)
{
if(s.equals(""))
{
tempArray[i]="0";
}
i++;
}
if(finalValue.equals(convert32Bit(tempArray)))
{
if(!newBit32.contains(ip))
{
String str = bit32.toString();
String findStr = ip;
int lastIndex = 0;
int count = 0;
while(lastIndex != -1){
lastIndex = str.indexOf(findStr,lastIndex);
if(lastIndex != -1){
count++;
lastIndex += findStr.length();
}
}
for(int k = 0; k<count;k++)
{
newBit32.add(ip);
}
}
}
}
}
}
BigInteger convert32Bit(String[] array)
{
int[] tempArray = new int[array.length];
ArrayList<BigInteger> tempBigIntList = new ArrayList<BigInteger>();
int i = 0;
for(String s:array)
{
int power = 4-i;
tempArray[i]= Integer.parseInt(s);
String string = Integer.toString(tempArray[i]);
BigInteger myBigInt = new BigInteger(string);
BigInteger num2 = myBigInt.multiply(new BigInteger("256").pow(power));
tempBigIntList.add(num2);
i++;
}
BigInteger bigInt32Bit = new BigInteger("0");
for(BigInteger bI:tempBigIntList)
{
bigInt32Bit = bigInt32Bit.add(bI);
}
return bigInt32Bit;
}
public void sort128BitIPs(ArrayList<String> bit128,ArrayList<String> newBit128)
{
ArrayList<BigInteger> bigInt128Bit = new ArrayList<BigInteger>();
for(String ip:bit128)
{
String[] tempArray = ip.split(":");
int i=0;
for(String s:tempArray)
{
if(s.equals(""))
{
tempArray[i]="0";
}
i++;
}
bigInt128Bit.add(convert128Bit(tempArray));
}
Collections.sort(bigInt128Bit);
for(BigInteger finalValue:bigInt128Bit)
{
for(String ip:bit128)
{
String[] tempArray = ip.split(":");
int i=0;
for(String s:tempArray)
{
if(s.equals(""))
{
tempArray[i]="0";
}
i++;
}
if(finalValue.equals(convert128Bit(tempArray)))
{
if(!newBit128.contains(ip))
{
String str = bit128.toString();
String findStr = ip;
int lastIndex = 0;
int count = 0;
while(lastIndex != -1){
lastIndex = str.indexOf(findStr,lastIndex);
if(lastIndex != -1){
count++;
lastIndex += findStr.length();
}
}
for(int k = 0; k<count;k++)
{
newBit128.add(ip);
}
}
}
}
}
}
BigInteger convert128Bit(String[] array)
{
int[] tempArray = new int[array.length];
ArrayList<BigInteger> tempBigIntList = new ArrayList<BigInteger>();
int i = 0;
for(String s:array)
{
int power = 8-i;
tempArray[i]= Integer.parseInt(s,16);
String string = Integer.toString(tempArray[i]);
BigInteger myBigInt = new BigInteger(string);
BigInteger num2 = myBigInt.multiply(new BigInteger("65536").pow(power));
tempBigIntList.add(num2);
i++;
}
BigInteger bigInt128Bit = new BigInteger("0");
for(BigInteger bI:tempBigIntList)
{
bigInt128Bit = bigInt128Bit.add(bI);
}
return bigInt128Bit;
}
public void printInOrder(ArrayList<String> bit32,ArrayList<String> bit128)
{
System.out.println("\nSorted IPs");
System.out.println("Sorted 32 bit IPs - Ascending");
for(String ip: bit32)
{
System.out.println(" "+ip);
}
Collections.reverse(bit32);
System.out.println("\nSorted 32 bit IPs - Descending");
for(String ip: bit32)
{
System.out.println(" "+ip);
}
System.out.println("\nSorted 128 bit IPs - Ascending");
for(String ip: bit128)
{
System.out.println(" "+ip);
}
Collections.reverse(bit128);
System.out.println("\nSorted 128 bit IPs - Descending");
for(String ip: bit128)
{
System.out.println(" "+ip);
}
}
public void run(ArrayList<String> bit32,ArrayList<String> bit128,ArrayList<String> newBit32,ArrayList<String> newBit128)
{
sortIntoRespectiveIPTypes();
sort32BitIPs(bit32,newBit32);
sort128BitIPs(bit128,newBit128);
printInOrder(newBit32,newBit128);
}
public static void main(String[] args)
{
IPSort ipS = new IPSort();
ipS.run(ipS.bit32,ipS.bit128,ipS.cleanBit32,ipS.cleanBit128);
}
}
As a note it is possible to use this class to sort IPs but my code does not use it
This code also sorts the list into an ascending order, then into a descending order. This is printed out in the command console when the code is run
Output
I would suggest to implement your own Comparator. See this post: Sorting IP addresses in Java
Copy paste only for you:
/**
* LGPL
*/
public class InetAddressComparator implements Comparator {
#Override
public int compare(InetAddress adr1, InetAddress adr2) {
byte[] ba1 = adr1.getAddress();
byte[] ba2 = adr2.getAddress();
// general ordering: ipv4 before ipv6
if(ba1.length < ba2.length) return -1;
if(ba1.length > ba2.length) return 1;
// we have 2 ips of the same type, so we have to compare each byte
for(int i = 0; i < ba1.length; i++) {
int b1 = unsignedByteToInt(ba1[i]);
int b2 = unsignedByteToInt(ba2[i]);
if(b1 == b2)
continue;
if(b1 < b2)
return -1;
else
return 1;
}
return 0;
}
private int unsignedByteToInt(byte b) {
return (int) b & 0xFF;
}
}
For the ip4 adresses you showed you just
need to split it up. then i would convert it to a long value, and sort by that.
long value = f3 + f2*256 + f1 * 256^2 + f0 * 256^3
where f0 - f3 are the splitted values.
Pad each fragment in IP to length 3 and then sort e.g. below:
List<String> ipList = new ArrayList<String>();
ipList.add("123.4.245.23");
ipList.add("104.244.253.29");
ipList.add("1.198.3.93");
ipList.add("32.183.93.40");
ipList.add("104.30.244.2");
ipList.add("104.244.4.1");
Collections.sort(ipList, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
String[] ips1 = o1.split("\\.");
String updatedIp1 = String.format("%3s.%3s.%3s.%3s",
ips1[0],ips1[1],ips1[2],ips1[3]);
String[] ips2 = o2.split("\\.");
String updatedIp2 = String.format("%3s.%3s.%3s.%3s",
ips2[0],ips2[1],ips2[2],ips2[3]);
return updatedIp1.compareTo(updatedIp2);
}
});
//print the sorted IP
for(String ip: ipList){
System.out.println(ip);
}
It prints:
1.198.3.93
32.183.93.40
104.30.244.2
104.244.4.1
104.244.253.29
123.4.245.23
public class IpSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] arr = {"192.168.1.1",
"191.122.123.112",
"192.161.1.1",
"191.122.123.1",
"123.24.5.78",
"121.24.5.78",
"123.24.4.78",
"123.2.5.78",
"192.1.1.1",
"125.45.67.89",
"1.1.1.1",
"3.4.5.6",
"2.2.2.2",
"6.6.6.7",
"155.155.23.0"};
String tmp;
for(int i=0;i<arr.length;i++)
{
for(int j=1;j<arr.length-i;j++)
{
String[] instr1 = arr[j-1].split("\\.");
String[] instr2 = arr[j].split("\\.");
if(Integer.parseInt(instr1[0]) > Integer.parseInt(instr2[0]))
{
tmp=arr[j-1];
arr[j-1]=arr[j];
arr[j]=tmp;
}else if(Integer.parseInt(instr1[0]) == Integer.parseInt(instr2[0])
&& Integer.parseInt(instr1[1]) > Integer.parseInt(instr2[1]) )
{
tmp=arr[j-1];
arr[j-1]=arr[j];
arr[j]=tmp;
} else if(Integer.parseInt(instr1[0]) == Integer.parseInt(instr2[0])
&& Integer.parseInt(instr1[1]) == Integer.parseInt(instr2[1])
&& Integer.parseInt(instr1[2]) > Integer.parseInt(instr2[2]) )
{
tmp=arr[j-1];
arr[j-1]=arr[j];
arr[j]=tmp;
} else if(Integer.parseInt(instr1[0]) == Integer.parseInt(instr2[0])
&& Integer.parseInt(instr1[1]) == Integer.parseInt(instr2[1])
&& Integer.parseInt(instr1[2]) == Integer.parseInt(instr2[2])
&& Integer.parseInt(instr1[3]) > Integer.parseInt(instr2[3]) )
{
tmp=arr[j-1];
arr[j-1]=arr[j];
arr[j]=tmp;
}
}
}
System.out.println("final sorted list of ips :\n");
for(int k=0;k<arr.length;k++){
System.out.println(arr[k]);
}
}
}
In Java8
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
class Test {
private static final Comparator<InetAddress> COMPARATOR = Comparator
.comparing(InetAddress::getAddress,
Comparator.comparingInt((byte[] b) -> b.length)
.thenComparing(b -> new BigInteger(1, b)));
public static void main(String[] args) {
final String[] addresses = {
"123.4.245.23",
"104.244.253.29",
"1.198.3.93",
"32.183.93.40",
"104.30.244.2",
"104.244.4.1"
};
for (final String address : sort(addresses)) {
System.out.println(address);
}
}
public static String[] sort(final String[] addresses) {
return Arrays.stream(addresses)
.map(s -> new AbstractMap.SimpleImmutableEntry<>(toInetAddress(s), s))
.sorted(Comparator.comparing(Map.Entry::getKey, Comparator.nullsLast(COMPARATOR)))
.map(Map.Entry::getValue)
.toArray(String[]::new);
}
private static InetAddress toInetAddress(final String address) {
try {
return InetAddress.getByName(address);
} catch (final UnknownHostException | SecurityException e) {
e.printStackTrace();
return null;
}
}
}
Output:
1.198.3.93
32.183.93.40
104.30.244.2
104.244.4.1
104.244.253.29
123.4.245.23
How about this plain logic:
Ip Address:
[10.1.1.2, 10.22.33.11, 10.12.23.12]
1) Fill in the IP to complete 12 digits format with prefix 0:
like
[010.001.001.002, 010.022.033.011, 010.012.023,012]
2) Remove "."s to make it complete string of digits:
[010001001002, 010022033011, 010012023012]
3) Apply Sort
[010001001002, 010012023012, 010022033011]
4) Retain dots after every 3 digits:
[010.001.001.002, 010.012.023.012, 010.022.033.011]
5) Remove prefix 0's
[10.1.1.2, 10.12.23.12, 10.22.33.11]
6) Sorted!
public class SortIP
{
public static String getFormattedIP(String ip)
{
String arg[] = new String[4];
arg = (ip).split("\\.");
int i=0;
while(i<=3)
{
if(arg[i].length()==1)
{
arg[i]="00"+arg[i];
}
else if(arg[i].length()==2)
{
arg[i]="0"+arg[i];
}
i++;
}
return arg[0]+arg[1]+arg[2]+arg[3];
}
public static ArrayList<Integer> sortedList(Object[] obj,String order)
{
if(order.equalsIgnoreCase("Ascending"))
{
Arrays.sort(obj, new Comparator() {
public int compare(Object o1, Object o2) {
return ((Map.Entry<Integer, Long>) o1).getValue()
.compareTo(((Map.Entry<Integer, Long>) o2).getValue());
}
});
}
else
{
Arrays.sort(obj, new Comparator() {
public int compare(Object o1, Object o2) {
return ((Map.Entry<Integer, Long>) o2).getValue()
.compareTo(((Map.Entry<Integer, Long>) o1).getValue());
}
});
}
int counter=0;
ArrayList<Integer> key = new ArrayList<Integer>();
//int key[] = new int[ipRange.size()];
for (Object e : obj) {
key.add(((Map.Entry<Integer, Long>) e).getKey());
//key[counter++]=((Map.Entry<Integer, Long>) e).getKey();
System.out.println(((Map.Entry<Integer, Long>) e).getKey() + " : " + ((Map.Entry<Integer, Long>) e).getValue());
}
return key;
}
public static void main(String[] args)
{
Map<Integer,String> ipRange= new TreeMap<Integer,String>();
Map<Integer,Long> formatedIpRange= new TreeMap<Integer,Long>();
ipRange.put(1, "10.1.4.100");
ipRange.put(2, "1.10.400.10");
ipRange.put(3, "196.0.14.15");
ipRange.put(4, "196.70.5.1");
ipRange.put(5, "196.70.7.3");
ipRange.put(6, "153.70.7.0");
for(int j=1;j<=ipRange.size();j++)
{
formatedIpRange.put(j, Long.parseLong(getFormattedIP(ipRange.get(j))));
}
Object[] a = formatedIpRange.entrySet().toArray();
ArrayList<Integer> key = sortedList(a,"descending");
System.out.println("ordered list ");
for (Integer integer : key)
{
System.out.println(ipRange.get(integer));
}
}
}
Try this one
#Override
public int compare(Object adr1, Object adr2) {
try
{
if(adr1 == null || adr1.toString().isEmpty()) return -1;
if(adr2 == null || adr2.toString().isEmpty()) return 1;
String[] ba1 = adr1.toString().split( "\\." );
String[] ba2 = adr2.toString().split( "\\." );
for ( int i = 0; i < ba1.length; i++ )
{
int b1 = Integer.parseInt( ba1[ i ] );
int b2 = Integer.parseInt( ba2[ i ] );
if (b1 == b2)
continue;
if (b1 < b2)
return -1;
else
return 1;
}
return 0;
}
catch ( Exception ex )
{
return 0;
}
}
This is an implementation in Perl, but it demonstrates the need to precompute the splits or factorings instead of computing them again and again for each comparison. I haven't tested it on mixed ipv4 & ipv6 but I think it should work. I just used the example. It seems like precomputing split values is the winner, then comparing the quads separately, instead of exponentiating and adding them all up.
2022-11-02 11:58:42 Wed $ cat | /tmp/foo.pl
123.4.245.23
104.244.253.29
1.198.3.93
32.183.93.40
104.30.244.2
104.244.4.1
Benchmark: timing 10000 iterations of factor_each_time, factor_precompute, split_each_time, split_precompute...
factor_each_time: 3 wallclock secs ( 2.94 usr + 0.00 sys = 2.94 CPU) # 3401.36/s (n=10000)
factor_precompute: 1 wallclock secs ( 1.03 usr + 0.00 sys = 1.03 CPU) # 9708.74/s (n=10000)
split_each_time: 3 wallclock secs ( 3.01 usr + 0.00 sys = 3.01 CPU) # 3322.26/s (n=10000)
split_precompute: 1 wallclock secs ( 1.00 usr + 0.00 sys = 1.00 CPU) # 10000.00/s (n=10000)
Code:
#!/usr/bin/perl
use strict;
use warnings FATAL => 'all';
use English '-no_match_vars';
use Regexp::Common;
use Benchmark qw(:all);
#use YAML;
my #lines = <STDIN>;
chomp $_ for #lines;
timethese( 10000, {
split_each_time => sub {
my #sorted = sort ip_each_time_split_sorter #lines;
#warn "split_each_time:\n".Dump(\#sorted);
},
split_precompute => sub {
my #sorted =
map $_->{num},
sort ip_precompute_split_sorter
map split_vals($_),
#lines
;
#warn "split_precompute:\n".Dump(\#sorted);
},
factor_each_time => sub {
my #sorted = sort ip_each_time_factor_sorter #lines;
#warn "factor_each_time:\n".Dump(\#sorted);
},
factor_precompute => sub {
my #sorted =
map $_->{num},
sort ip_precompute_factor_sorter
map factor_vals($_),
#lines
;
#warn "factor_precompute:\n".Dump(\#sorted);
},
} );
sub ip_each_time_factor_sorter {
our ($a, $b);
my $a_factor = factor_vals($a);
my $b_factor = factor_vals($b);
return -1 if exists $a_factor->{ipv4} && exists $b_factor->{ipv6};
return 1 if exists $a_factor->{ipv6} && exists $b_factor->{ipv4};
return $a_factor->{val} <=> $b_factor->{val};
}
sub ip_precompute_factor_sorter {
our ($a, $b);
return -1 if exists $a->{ipv4} && exists $b->{ipv6};
return 1 if exists $a->{ipv6} && exists $b->{ipv4};
return $a->{val} <=> $b->{val};
}
sub factor_vals {
my ($ip) = #_;
my $xform;
if ($ip =~ m{ \A $RE{net}{IPv4}{-keep} \z }mxs) {
$xform = {
ipv4 => 1,
num => $1,
val => (
($2 || 0) * 256 ** 4
+ ($3 || 0) * 256 ** 3
+ ($4 || 0) * 256 ** 2
+ ($5 || 0) * 256 ** 1
),
};
}
elsif ($ip =~ m{ \A $RE{net}{IPv6}{-keep} \z }mxs) {
$xform = {
ipv6 => 1,
num => $1,
val => (
($2 || 0) * 65536 ** 8
+ ($3 || 0) * 65536 ** 7
+ ($4 || 0) * 65536 ** 6
+ ($5 || 0) * 65536 ** 5
+ ($6 || 0) * 65536 ** 4
+ ($7 || 0) * 65536 ** 3
+ ($8 || 0) * 65536 ** 2
+ ($9 || 0) * 65536 ** 1
),
};
}
else {
warn "skipping bad entry: $_\n";
}
return $xform;
}
sub split_vals {
my ($ip) = #_;
my $xform;
if ($ip =~ m{ \A $RE{net}{IPv4}{-keep} \z }mxs) {
$xform = {
ipv4 => 1,
num => $1,
vals => [
$2 || 0,
$3 || 0,
$4 || 0,
$5 || 0,
],
};
}
elsif ($ip =~ m{ \A $RE{net}{IPv6}{-keep} \z }mxs) {
$xform = {
ipv6 => 1,
num => $1,
vals => [
$2 || 0,
$3 || 0,
$4 || 0,
$5 || 0,
$6 || 0,
$7 || 0,
$8 || 0,
$9 || 0,
],
};
}
else {
warn "skipping bad entry: $_\n";
}
return $xform;
}
sub ip_precompute_split_sorter {
our ($a, $b);
return -1 if exists $a->{ipv4} && exists $b->{ipv6};
return 1 if exists $a->{ipv6} && exists $b->{ipv4};
my $a_vals = $a->{vals};
my $b_vals = $b->{vals};
for my $i (0..$#{$a_vals}) {
my $sortval = $a_vals->[$i] <=> $b_vals->[$i];
return $sortval if $sortval != 0;
}
return 0;
}
sub ip_each_time_split_sorter {
our ($a, $b);
my $a_split = split_vals($a);
my $b_split = split_vals($b);
return -1 if exists $a_split->{ipv4} && exists $b_split->{ipv6};
return 1 if exists $a_split->{ipv6} && exists $b_split->{ipv4};
my $a_vals = $a_split->{vals};
my $b_vals = $b_split->{vals};
for my $i (0..$#{$a_vals}) {
my $sortval = $a_vals->[$i] <=> $b_vals->[$i];
return $sortval if $sortval != 0;
}
return 0;
}
I am creating a table of contents, and what I have is a Map of product numbers to pages. So an entry might look like this:
ABC123 => [59, 58, 57, 19, 36, 15, 33, 34, 13, 39, 11, 37, 38, 21, 20, 40, 63, 60, 45, 46, 22, 23, 24, 26, 3, 2, 10, 1, 7, 6, 5, 4, 8]
What I want to get from this is:
1-8,10,11,13,15,19-24,26,33,34,36-38,40,45,46,57-60
I can code this of course, but I figured that someone else has already solved this problem. My Googling has yielded naught.
I appreciate any help you can offer, as always!
You could collect the numbers into a sorted set and then iterate over the numbers.
Quick and dirty example:
SortedSet<Integer> numbers = new TreeSet<Integer>();
numbers.add( 1 );
numbers.add( 2 );
numbers.add( 3 );
numbers.add( 6 );
numbers.add( 7 );
numbers.add( 10 );
Integer start = null;
Integer end = null;
for( Integer num : numbers ) {
//initialize
if( start == null || end == null ) {
start = num;
end = num;
}
//next number in range
else if( end.equals( num - 1 ) ) {
end = num;
}
//there's a gap
else {
//range length 1
if( start.equals( end )) {
System.out.print(start + ",");
}
//range length 2
else if ( start.equals( end - 1 )) {
System.out.print(start + "," + end + ",");
}
//range lenth 2+
else {
System.out.print(start + "-" + end + ",");
}
start = num;
end = num;
}
}
if( start.equals( end )) {
System.out.print(start);
}
else if ( start.equals( end - 1 )) {
System.out.print(start + "," + end );
}
else {
System.out.print(start + "-" + end);
}
Yields: 1-3,6,7,10
Apache Commons has the IntRange type that you can use. Unfortunately I didn't find a good corresponding set of utilities to create them. Here's the basic approach you could use:
//create a list of 1-integer ranges
List<IntRange> ranges = new LinkedList<IntRange>();
for ( int pageNum : pageNums ) {
ranges.add(new IntRange(pageNum));
}
//sort the ranges
Collections.sort(ranges, new Comparator<IntRange>() {
public int compare(IntRange a, IntRange b) {
return Integer.valueOf(a.getMinimumInteger()).compareTo(b.getMinimumInteger());
}
});
List<IntRange> output = new ArrayList<IntRange>();
if ( ranges.isEmpty() ) {
return output;
}
//collapse consecutive ranges
IntRange range = ranges.remove(0);
while ( !ranges.isEmpty() ) {
IntRange nextRange = ranges.remove(0);
if ( range.getMaximumInteger() == nextRange.getMinimumInteger() - 1 ) {
range = new IntRange(range.getMinimumInteger(), nextRange.getMaximumInteger());
} else {
output.add(range);
range = nextRange;
}
}
output.add(range);
Alternatively you could skip the first step and create the ranges directly from the sorted list of page numbers.
Edit: A better description:
I had to deal with something similar to support a sorted collection of finite ranges, I used a mix of Google's Guava Range class and binary search to insert the element at the corresponding range or create a new singleton Range (A range with 1 element), eventually with more inserts the ranges have chances of expanding (Or shrinking/splitting in case of removal), removal is pretty fast because locating the corresponding range where the element is uses a binary search:
import com.google.common.collect.DiscreteDomains;
import com.google.common.collect.Lists;
import com.google.common.collect.Range;
import com.google.common.collect.Ranges;
import java.util.Collection;
import java.util.List;
public class IntRangeCollection
{
private int factor=10;
private List<Range<Integer>> rangeList=null;
public IntRangeCollection()
{
rangeList=Lists.newArrayListWithExpectedSize(1000);
}
public IntRangeCollection(final int size)
{
rangeList=Lists.newArrayListWithExpectedSize(size);
}
public IntRangeCollection(final int size, final int factor)
{
rangeList=Lists.newArrayListWithExpectedSize(size);
this.factor=factor;
}
protected IntRangeCollection(final List<Range<Integer>> rangeList)
{
this.rangeList=rangeList;
}
public static IntRangeCollection buildIntRangesCollectionFromArrays(final List<Integer[]> arrays)
{
final List<Range<Integer>> rangeList=Lists.newArrayListWithCapacity(arrays.size());
for(Integer[] range : arrays){
rangeList.add(range.length == 1 ? Ranges.singleton(range[0]) : Ranges.closed(range[0], range[1]));
}
return new IntRangeCollection(rangeList);
}
public boolean addElements(final Collection<Integer> elements)
{
boolean modified=false;
for(Integer element : elements){
modified=addElement(element) || modified;
}
return modified;
}
public boolean removeElements(final Collection<Integer> elements)
{
boolean modified=false;
for(Integer element : elements){
modified=removeElement(element) || modified;
}
return modified;
}
public boolean addElement(final Integer element)
{
final Range<Integer> elementRange=Ranges.singleton(element);
if(rangeList.isEmpty()){
rangeList.add(elementRange);
} else{
int
start=0, mid=0,
end=rangeList.size() - 1;
Range<Integer> midRange=null;
while(start<=end){
mid=(start + end) / 2;
midRange=rangeList.get(mid);
if(midRange.contains(element)){
return false;
} else if(testLinkable(midRange, element)){
rangeList.set(mid, midRange.span(elementRange));
if(mid>0){
final Range<Integer> a=rangeList.get(mid - 1);
if(testLinkable(a, midRange)){
rangeList.set(mid - 1, a.span(midRange));
rangeList.remove(mid);
mid--;
}
}
if(mid<rangeList.size() - 1){
final Range<Integer> b=rangeList.get(mid + 1);
if(testLinkable(midRange, b)){
rangeList.set(mid, midRange.span(b));
rangeList.remove(mid + 1);
}
}
return true;
} else if(midRange.lowerEndpoint().compareTo(element)<0){
start=mid + 1;
} else{
end=mid - 1;
}
}
//noinspection ConstantConditions
rangeList.add(midRange.lowerEndpoint().compareTo(element)<0 ? mid + 1 : mid, elementRange);
}
return true;
}
public boolean removeElement(final Integer element)
{
final Range<Integer> elementRange=Ranges.singleton(element);
if(rangeList.isEmpty()){
rangeList.add(elementRange);
} else{
int
start=0, mid,
end=rangeList.size() - 1;
while(start<=end){
mid=(start + end) / 2;
final Range<Integer> midRange=rangeList.get(mid);
if(midRange.contains(element)){
final Integer
lower=midRange.lowerEndpoint(),
upper=midRange.upperEndpoint();
if(lower.equals(upper)){
rangeList.remove(mid);
} else if(lower.equals(element)){
rangeList.set(mid, Ranges.closed(element + 1, upper));
} else if(upper.equals(element)){
rangeList.set(mid, Ranges.closed(lower, element - 1));
} else{
rangeList.set(mid, Ranges.closed(element + 1, upper));
rangeList.add(mid, Ranges.closed(lower, element - 1));
}
return true;
} else if(midRange.lowerEndpoint().compareTo(element)<0){
start=mid + 1;
} else{
end=mid - 1;
}
}
}
return false;
}
public List<Integer> getElementsAsList()
{
final List<Integer> result=Lists.newArrayListWithExpectedSize(rangeList.size() * factor);
for(Range<Integer> range : rangeList){
result.addAll(range.asSet(DiscreteDomains.integers()));
}
return result;
}
public List<Integer[]> getRangesAsArray()
{
final List<Integer[]> result=Lists.newArrayListWithCapacity(rangeList.size());
for(Range<Integer> range : rangeList){
final Integer
lower=range.lowerEndpoint(),
upper=range.upperEndpoint();
result.add(lower.equals(upper) ? new Integer[]{lower} : new Integer[]{lower,upper});
}
return result;
}
public int getRangesCount()
{
return rangeList.size();
}
private boolean testLinkable(final Range<Integer> range, final Integer element)
{
return Ranges.closed(range.lowerEndpoint() - 1, range.upperEndpoint() + 1).contains(element);
}
private boolean testLinkable(final Range<Integer> a, final Range<Integer> b)
{
return Ranges.closed(a.lowerEndpoint() - 1, a.upperEndpoint() + 1).isConnected(b);
}
#Override
public String toString()
{
return "IntRangeCollection{" +
"rangeList=" + rangeList +
'}';
}
public static void main(String[] args)
{
final int MAX_NUMBER=1000;
final long startMillis=System.currentTimeMillis();
final IntRangeCollection ranges=new IntRangeCollection();
for(int i=0; i<MAX_NUMBER; i++){
//noinspection UnsecureRandomNumberGeneration
ranges.addElement((int) (Math.random() * MAX_NUMBER));
}
System.out.println(MAX_NUMBER + " contained in " + ranges.rangeList.size() + " ranges done in " + (System.currentTimeMillis() - startMillis) + "ms");
System.out.println(ranges);
for(int i=0; i<MAX_NUMBER / 4; i++){
//noinspection UnsecureRandomNumberGeneration
ranges.removeElement((int) (Math.random() * MAX_NUMBER));
}
System.out.println(MAX_NUMBER + " contained in " + ranges.rangeList.size() + " ranges done in " + (System.currentTimeMillis() - startMillis) + "ms");
System.out.println(ranges);
}
}
You can use Arrays.sort() and find neighbouring duplicates/ranges. However I suspect TreeSet may be simpler to use.
This is a good example, it shows a simple way to accomplish this.