DecimalFormat removes zero after dot - java

I want to format user's input and it's okay, but when I try to input zero after dot DecimalFormat removes it.
I use the following code:
DecimalFormat df = new DecimalFormat("#,###.##");
Number n = df.parse(v);
amountEdit.setText(df.format(n));
Example Input/Output:
9.0 -> 9.
9.9 -> 9.9
9.90 -> 9.9
It removes zeros!
EDIT:
I have EditText with TextChangedListener
The idea is to format user's input like 999 999 999.99 (this is max value).
amountEdit.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (data.document.isPaymentPossible) {
if (s.toString().contains(String.valueOf(df.getDecimalFormatSymbols().getDecimalSeparator())) || s.toString().contains(".")) {
hasDot = true;
} else {
hasDot = false;
}
}
}
#Override
public void afterTextChanged(Editable s) {
String string = s.toString().replaceAll("\\.", ",");
if (string.equals(",") || string.equals(".")) {
amountEdit.setText("");
return;
}
amountEdit.removeTextChangedListener(this);
payCustomAmount.setEnabled(amountEdit.getText().length() != 0);
try {
if (string.contains(",")) {
try {
String afterDot = string.split(",")[1];
if (afterDot.length() > 2) {
string = string.substring(0, string.length() - 1);
Number n = df.parse(string);
amountEdit.setText(df.format(n).replace(",", "."));
amountEdit.setSelection(amountEdit.getText().length());
amountEdit.addTextChangedListener(this);
showOverPaidText();
return;
}
} catch (Exception e) {
if (BuildConfig.DEBUG) {
SysUtils.logf("PaymentOptions input: " + s + "Exception: " + e);
}
}
} else {
if (string.length() > 11) {
string = string.substring(0, string.length() - 1);
Number n = dfnd.parse(string);
amountEdit.setText(dfnd.format(n));
amountEdit.setSelection(amountEdit.getText().length());
showOverPaidText();
amountEdit.addTextChangedListener(this);
return;
}
}
int inilen, endlen;
inilen = amountEdit.getText().length();
String v = string.replace(String.valueOf(df.getDecimalFormatSymbols().getGroupingSeparator()), "");
int cp = amountEdit.getSelectionStart();
if (hasDot) {
Number n = df.parse(v);
String ss = df.format(n).replace(",", ".");
amountEdit.setText(ss);
} else {
Number n = dfnd.parse(v);
amountEdit.setText(dfnd.format(n));
}
endlen = amountEdit.getText().length();
int sel = (cp + (endlen - inilen));
if (sel > 0 && sel <= amountEdit.getText().length()) {
amountEdit.setSelection(sel);
} else {
amountEdit.setSelection(amountEdit.getText().length() - 1);
}
} catch (NumberFormatException | ParseException e) {
showOverPaidText();
amountEdit.addTextChangedListener(this);
if (BuildConfig.DEBUG) {
SysUtils.logf("PaymentOptions input: " + s + "Exception: " + e);
}
return;
}
showOverPaidText();
amountEdit.addTextChangedListener(this);
return;
}
});
My onCreate contains:
df = new DecimalFormat("#,###.00");
df.setDecimalSeparatorAlwaysShown(true);
dfnd = new DecimalFormat("#,###");
hasDot = false;

It removes zeros!
Well yes, it would - you've specifically used .## which means "only include digits if they're significant". If you want to always have at least one decimal place, use
DecimalFormat df = new DecimalFormat("#,###.0#");
If you always want to have two decimal places, use:
DecimalFormat df = new DecimalFormat("#,###.00");
You should probably consider how you want 0.5 to be formatted, too. Do you want "0.5" or ".5"?

'#' means include this digit if it matters and since 9,0 == 9, the formatting removes it.
If you need a minimum of two digits after decimal then you should replace to format as:
DecimalFormat df = new DecimalFormat("#,###.00");

Related

Convert given time in String format to seconds in Android

Suppose time is given in MM:SS(ex- 02:30) OR HH:MM:SS in String format.how can we convert this time to second.
In your case, using your example you could use something like the following:
String time = "02:30"; //mm:ss
String[] units = time.split(":"); //will break the string up into an array
int minutes = Integer.parseInt(units[0]); //first element
int seconds = Integer.parseInt(units[1]); //second element
int duration = 60 * minutes + seconds; //add up our values
If you want to include hours just modify the code above and multiply hours by 3600 which is the number of seconds in an hour.
public class TimeToSeconds {
// given: mm:ss or hh:mm:ss or hhh:mm:ss, return number of seconds.
// bad input throws NumberFormatException.
// bad includes: "", null, :50, 5:-4
public static long parseTime(String str) throws NumberFormatException {
if (str == null)
throw new NumberFormatException("parseTimeString null str");
if (str.isEmpty())
throw new NumberFormatException("parseTimeString empty str");
int h = 0;
int m, s;
String units[] = str.split(":");
assert (units.length == 2 || units.length == 3);
switch (units.length) {
case 2:
// mm:ss
m = Integer.parseInt(units[0]);
s = Integer.parseInt(units[1]);
break;
case 3:
// hh:mm:ss
h = Integer.parseInt(units[0]);
m = Integer.parseInt(units[1]);
s = Integer.parseInt(units[2]);
break;
default:
throw new NumberFormatException("parseTimeString failed:" + str);
}
if (m<0 || m>60 || s<0 || s>60 || h<0)
throw new NumberFormatException("parseTimeString range error:" + str);
return h * 3600 + m * 60 + s;
}
// given time string (hours:minutes:seconds, or mm:ss, return number of seconds.
public static long parseTimeStringToSeconds(String str) {
try {
return parseTime(str);
} catch (NumberFormatException nfe) {
return 0;
}
}
}
import org.junit.Test;
import static org.junit.Assert.*;
public class TimeToSecondsTest {
#Test
public void parseTimeStringToSeconds() {
assertEquals(TimeToSeconds.parseTimeStringToSeconds("1:00"), 60);
assertEquals(TimeToSeconds.parseTimeStringToSeconds("00:55"), 55);
assertEquals(TimeToSeconds.parseTimeStringToSeconds("5:55"), 5 * 60 + 55);
assertEquals(TimeToSeconds.parseTimeStringToSeconds(""), 0);
assertEquals(TimeToSeconds.parseTimeStringToSeconds("6:01:05"), 6 * 3600 + 1*60 + 5);
}
#Test
public void parseTime() {
// make sure all these tests fail.
String fails[] = {null, "", "abc", ":::", "A:B:C", "1:2:3:4", "1:99", "1:99:05", ":50", "-4:32", "-99:-2:4", "2.2:30"};
for (String t: fails)
{
try {
long seconds = TimeToSeconds.parseTime(t);
assertFalse("FAIL: Expected failure:"+t+" got "+seconds, true);
} catch (NumberFormatException nfe)
{
assertNotNull(nfe);
assertTrue(nfe instanceof NumberFormatException);
// expected this nfe.
}
}
}
}
int v = 0;
for (var x: t.split(":")) {
v = v * 60 + new Byte(x);
}
This snippet should support HH:MM:SS (v would result in seconds) or HH:MM (v would be in minutes)
try this
hours = totalSecs / 3600;
minutes = (totalSecs % 3600) / 60;
seconds = totalSecs % 60;
timeString = String.format("%02d",seconds);
private static final String TIME_FORMAT = "hh:mm a";//give whatever format you want.
//Function calling
long timeInMillis = TimeUtils.getCurrentTimeInMillis("04:21 PM");
long seconds = timeInMillis/1000;
//Util Function
public static long getCurrentTimeInMillis(String time) {
SimpleDateFormat sdf = new SimpleDateFormat(TIME_FORMAT, Locale.getDefault());
// sdf.setTimeZone(TimeZone.getTimeZone("GMT")); //getting exact milliseconds at GMT
// sdf.setTimeZone(TimeZone.getDefault());
Date date = null;
try {
date = sdf.parse(time);
} catch (ParseException e) {
e.printStackTrace();
}
return date.getTime();
}
I have written an extension function in Kotlin for converting String to seconds
fun String?.converTimeToSeconds(): Int {
if (this.isNullOrEmpty().not()) {
val units = this?.split(":")?.toTypedArray()
if (units?.isNotEmpty() == true && units.size >= 3) {
val hours = units[0].toInt()
val minutes = units[1].toInt()
val seconds = units[2].toInt()
return (3660 * hours) + (60 * minutes) + seconds
}
}
return 0
}

How can I format a String number to have commas in android Edit Field

For what function I can use in android to display the number into different formats.
For eg:
If I enter 1000 then it should display like this 1,000.
If I enter 10000 then it should display like this 10,000.
If I enter 1000000 then it should display like this 1,000,000.
Please guide me.
You could use DecimalFormat and just format the number
DecimalFormat formatter = new DecimalFormat("#,###,###");
String yourFormattedString = formatter.format(100000);
The result will be
1,000,000 for 1000000
10,000 for 10000
1,000 for 1000
Update 12/02/2019
This String.format("%,d", number) would be a better(less hardcoded) solution as indicated in the comments below by #DreaminginCode so I thought I would add it here as an alternative
try this one hope it will help.
System.out.println(NumberFormat.getNumberInstance(Locale.US).format(1000));
private String getFormatedAmount(int amount){
return NumberFormat.getNumberInstance(Locale.US).format(amount);
}
int[] numbersToFormat = new int[]
{ 1, 10, 100, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
for (int number : numbersToFormat) {
System.out.println(
NumberFormat.getNumberInstance(Locale.getDefault()).format(number));
}
OUTPUT
1
10
100
10,000
100,000
1,000,000
10,000,000
100,000,000
1,000,000,000
Add this function in common class
public static String getFormatedNumber(String number){
if(!number.isEmpty()) {
double val = Double.parseDouble(number);
return NumberFormat.getNumberInstance(Locale.US).format(val);
}else{
return "0";
}
}
And use that function every where like this:
String newNumber = Utils.getFormatedNumber("10000000");
You can use Numberformat
public static double getNumberByString(String s) throws ParseException {
return NumberFormat.getInstance(Locale.getDefault()).parse(s).doubleValue();
}
Add a text change listener as below (Also make sure that the input type selected for Edittext is Number) :
etTest.addTextChangedListener(new TextWatcher() {
boolean isManualChange = false;
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
if (isManualChange) {
isManualChange = false;
return;
}
try {
String value = s.toString().replace(",", "");
String reverseValue = new StringBuilder(value).reverse()
.toString();
StringBuilder finalValue = new StringBuilder();
for (int i = 1; i <= reverseValue.length(); i++) {
char val = reverseValue.charAt(i - 1);
finalValue.append(val);
if (i % 3 == 0 && i != reverseValue.length() && i > 0) {
finalValue.append(",");
}
}
isManualChange = true;
etTest.setText(finalValue.reverse());
etTest.setSelection(finalValue.length());
} catch (Exception e) {
// Do nothing since not a number
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
public static String formatNumber(int number){
return NumberFormat.getNumberInstance(Locale.getDefault()).format(number);
}
public static String formatNumber(String number){
return NumberFormat.getNumberInstance(Locale.getDefault()).format(Integer.parseInt(number));
}
I wrote this Kotlin extension function may can help.
fun String.formatPoint(): String {
val sb = StringBuilder()
this.reversed().forEachIndexed { index, c ->
// 123,123,123
if ((index + 1) % 3 == 0) {
if (index != this.length - 1) {
sb.append("$c,")
} else {
sb.append(c)
}
} else {
sb.append(c)
}
}
return sb.toString().reversed()
}
Recommended and preferred way is to do it with the strings.xml file
<string name="format_denominated">%,d</string>
from your Kotlin/Java code
getResources().getString(R.string.format_denominated, value)
Even better with databinding
<TextView
android:text="#{#string/format_denominated(value)}"
............/>

How to sort ip address in ascending order

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;
}

junit arrays not equal test

I'm trying to write a test case where my scenario is that two byte arrays should be not equal.
Can I do this with junit?
Or do I have to use something external like Hamcrest? I couldn't change the code in this answer to do the job
Please give a sample.
You can use
assertFalse(Arrays.equals(array1, array2));
If you wanted to check they were equal, I would use the following instead.
assertEquals(Arrays.toString(array1), Arrays.toString(array2));
as this produces a readable output as to what was different rather than just failing.
I prefer doing this the Hamcrest way, which is more expressive:
Assert.assertThat(array1, IsNot.not(IsEqual.equalTo(array2)));
Or the short version with static imports:
assertThat(array1, not(equalTo(array2)));
(The IsEqual matcher is smart enough to understand arrays, fortunately.)
Note that a limited version of Hamcrest is part of the JUnit 4.x distribution, so you don't need to add an external library.
Newer versions of JUnit offer org.junit.Assert.assertArrayEquals(byte[], byte[]), with overloads for other array types. Failures show the first index with a non-match and the differing elements at that index.
I also enjoy assertEquals(Arrays.asList(expected), Arrays.asList(actual)). The Hamcrest-powered rendition mentioned above is probably best.
Here is a possible alternative, which has the advantage of using the same code as assertArrayEquals() :
private void assertArrayNotEquals(byte[] expecteds, byte[] actuals) {
try {
assertArrayEquals(expecteds, actuals);
} catch (AssertionError e) {
return;
}
fail("The arrays are equal");
}
You could do it like this:
assertNotEquals(arrayOne, arrayTwo)
Sorry this is a bit long but it's easy to debug with and you can cut and paste it into your unit test.
private int span = 10;
private boolean equal(byte[] expected, byte[] got) {
final boolean result;
String message = null;
int offset = -1;
int length = -1;
if(expected == null && got == null) {
result = true;
} else if(expected == null || got == null) {
message = "One array is null: " + (expected == null ? "expected" : "got");
result = false;
} else if(expected.length != got.length) {
message = "Lengths differ: expected = " + expected.length + ", got = " + got.length;
result = false;
} else {
length = expected.length;
for(int i = 0; i < length; i++) {
if(expected[i] != got[i]) {
offset = i;
break;
}
}
result = offset == -1;
if(!result) {
message = "Contents differ";
}
}
if(!result) {
System.err.println(message);
if(offset >= 0) {
hexDump("Expected: ", expected, offset, length);
hexDump(" Got: ", got, offset, length);
}
}
return result;
}
private void hexDump(String label, byte[] ba, int offset, int length) {
System.err.print(label);
if(ba == null) {
System.err.println("<null>");
} else if(ba.length == 0) {
System.err.println("<zero-length-array>");
} else {
// <span> bytes either side
final int from = Math.max(0, offset - span);
final int to = Math.min(length, offset + span);
if(from != 0) {
System.err.print("(offset:" + from + ") ");
}
for(int i = from; i < to; i++) {
System.err.printf("%02X ", new Byte(ba[i]));
}
System.err.println();
}
}
#Test
public void testExample() {
assertTrue(equal(new byte[] { 1, 2, 3 }, new byte[] { 1, 8, 3 }));
}

Is there a Java library that will create a number range from a list of numbers?

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.

Categories

Resources