Treeset subsets - scjp

Hi I am having trouble understanding why the output is 'ex [6, 7, b, d] [6, 7, b]' for
this piece of code. Please can someone advise how the subset is working with the
numbers and letters? thanks
import java.util.*;
public class Corner {
public static void main(String[] args) {
TreeSet<String> t1 = new TreeSet<String>();
TreeSet<String> t2 = new TreeSet<String>();
t1.add("b"); t1.add("7");
t2 = (TreeSet)t1.subSet("5", "c");
try {
t1.add("d");
t2.add("6");
t2.add("3");
}
catch (Exception e) { System.out.print("ex "); }
System.out.println(t1 + " " + t2);
} }

I have made a few changes to clean up your code (usage of raw generic type and unchecked conversion) and to make the log output somewhat more insightful:
import java.util.SortedSet;
import java.util.TreeSet;
public class Corner {
public static void main(String[] args) {
SortedSet<String> t1 = new TreeSet<String>();
SortedSet<String> t2 = new TreeSet<String>();
t1.add("b");
t1.add("7");
System.out.println(t1 + " " + t2);
t2 = t1.subSet("5", "c");
System.out.println(t1 + " " + t2);
t1.add("d");
System.out.println(t1 + " " + t2);
t2.add("6");
System.out.println(t1 + " " + t2);
try {
t2.add("3");
} catch (Exception e) {
e.printStackTrace(System.out);
}
System.out.println(t1 + " " + t2);
}
}
The output looks like this:
[7, b] []
[7, b] [7, b]
[7, b, d] [7, b]
[6, 7, b, d] [6, 7, b]
java.lang.IllegalArgumentException: key out of range
at java.util.TreeMap$NavigableSubMap.put(Unknown Source)
at java.util.TreeSet.add(Unknown Source)
at Corner.main(Corner.java:18)
[6, 7, b, d] [6, 7, b]
Okay, after insertion of "b" and "7", t1 contains those elements in ASCII (or Unicode) sort order. t2 is empty. No surprise here.
After the subSet call, both sets have identical content because the given range "5" to "c" spans the whole current range of the original set from "7" to "b". No surprise either.
Please note: The subset is backed by the original set as described by the API JavaDoc.
Furthermore the API description says that the returned set will throw an IllegalArgumentException on an attempt to insert an element outside its range. This will be important later on.
Okay, next you add element "d" to t1 which is shown in t1, but not in t2 because "d" is outside the range "5" to "c" of t2.
Now you add element "6" to t2 (which is still backed by t1!). It is in the correct range of t2 and thus successfully added to both logical sets.
Now you run into trouble because you try to add "3" to t2, which is out of range "5" to "c", thus causing the IllegalArgumentException which can be seen in the log output. The element is not inserted into t2 (and thus not into t1 either). Consequently it is not shown in the last line of log output.
Bottom line: Your program behaves just as expected according to the JDK documentation. :-)

Related

Continue zip(), if one source is completed

I have some troubles with .zip() operator.
Let me simplify my problem on a small example.
Flux<Integer> flux1 = Flux.just(9, 8, 3, -2);
Flux<Integer> flux2 = Flux.just(7);
Flux<Integer> flux3 = Flux.just(6, 5, 4, -4);
List<Flux<Integer>> list1 = Arrays.asList(flux1, flux2, flux3);
TreeSet<Integer> set = new TreeSet<>(Comparator.reverseOrder());
Set<Integer> list = Flux.zip(list1, objects -> {
boolean setChanged = false;
for (Object o : objects) {
Integer i = (Integer) o;
if (set.size() < 5 || i > set.last()) {
setChanged = true;
set.add(i);
if (set.size() > 5) {
set.pollLast();
}
}
}
return setChanged;
}).takeWhile(val -> val)
.then(Mono.just(set))
.block();
System.out.println(list);
Here I have 3 different sources(they are sorted descending by default, and the number of them could be much bigger), and I want to get from them a collection of 5 elements sorted descending. Unfortunately, I can't just use concat() or merge() operators, because sources in a real life can be really big, but I need only small amount of elements.
I am expecting [9, 8, 7, 6, 5] here, but one of the sources is completed after first iteration of zipping.
Could you please suggest how I can get around with this problem?
You can try the reduce operation
#Test
void test() {
Flux<Integer> flux1 = Flux.just(9, 8, 3, -2);
Flux<Integer> flux2 = Flux.just(7, 0, -2, 4,3,2,2,1);
Flux<Integer> flux3 = Flux.just(6, 5, 4, -4);
var k = 5;
List<Flux<Integer>> publishers = List.of(flux1, flux2, flux3);
var flux = Flux.merge(publishers)
.log()
.limitRate(2)
.buffer(2)
.reduce((l1, l2) -> {
System.out.println(l1);
System.out.println(l2);
return Stream.concat(
l1.stream(),
l2.stream()
).sorted(Comparator.reverseOrder())
.limit(k)
.collect(Collectors.toList());
})
.log();
StepVerifier.create(flux)
.expectNext(List.of(9,8,7,6,5))
.expectComplete()
.verify();
}
You can fetch data in chunks and compare them to find the top K elements.
In a sequential case it will fetch a new batch, compare it to the current top k result and return a new topk like in the example above (PriorityQueue may work better for sorting if k is big).
If you're using parallel schedulers and batches are fetched in parallel, then it can compare them with each other independently that should be a bit faster.
Also you have full control over the fetched data via rateLimit, buffer, delayElements, etc

How to split a String and insert each value as a record in a database?

I am reading from a file where each line has 3-4 values that are separated by a tab. I have been trying to split the line up (based on the tabs) and store them in their own String, and then I push these values into a method that makes an SQL statement to INSERT INTO the values into a table as a new record.
My issue is that the split() method isn't quite working for me as it is giving me an `ArrayOutOfBoundsException' for the second value. But I am pretty sure there is an easier way to do what I am trying to do.
Currently I have:
// --------- Reading the topURLs file ---------
try {
scan = new Scanner (new File("TestURL"));
while (scan.hasNextLine()) {
String line = scan.nextLine();
String[] SQLValues = line.split("\\t");
String rank = SQLValues[0];
String websiteName = SQLValues[1];
String domain = SQLValues[2];
insertInto(conn, tableName, rank, websiteName, domain);
}
} catch (Exception e) {
e.printStackTrace();
}
And my insertIntomethod in case you would like to see:
public static void insertInto(Connection conn, String name, String rank, String websiteName, String domain) {
Statement st = null;
int rankInt = Integer.parseInt(rank);
try {
st = conn.createStatement();
st.execute("INSERT INTO " + name + "VALUES ('" + rankInt + "', '" + websiteName + "', '" + domain + "')");
} catch (SQLException e) {
e.printStackTrace();
}
}
I am aware this is messy, it's just all I know so far. The file TestURLs currently has two lines for the sake of testing (they are split using tabs in my file, I just don't know how to show that on here):
1 google com
2 cheese com
Is there something I am missing that causes the exception mentioned above? I thought that when I split line that it would put each separated word in the a different index of the array wordsArray. Instead, it seems like there is only one element in it at index 0.
If there is a better way to do what I am trying to do - that is, to take a line from a file, split the values, and put them into a table as a new record - could you point me in the right direction?
Any help is much appreciated.
try using
String[] SQLValues = line.split("\\s+");
you can use a greedy regular expression, which will match any number of white spaces including tabs. You can use "\s+" regex for this purpose.

SQLite with Android Studio, return all records selected by 2 arguments

let's assume that i have a table with columns such as:
ID SSID BSSID RSSI
1 abcd hs:hd:sd -60
2 abcd hs:hd:po -68
There are about 5000 records with the same SSID, slighltly different BSSID and the LEVEL values. My device is scanning the nearest environment for WiFi networks, therefore I know their MAC address and level of RSSI. I pick 3 with the highest value od RSSI.
First thing I would like to know if it is possible to search through the database to get all the records with the LEVEL value equal or close to 60, for instance 59,58,61.
Secondly, is there a way to query the database to return all the records with the same MAC addresses and RSSI values as from the 3 best scan result? If so, how would that query look like?
EDIT: Thanks for all the answers. What I'm trying to do now is to compare 3 scans with records stored in database with getRequiredData function. I would like to pass 2 parameters to this function, mac address and level and find records with same value for both parameters. The rawQuery seems to be fine, code is compiling but the app is crashing with the first scan. I cant find the cause of it, is it because my logic of getting these parameters is wrong or does it have something to do with query?
public Cursor getRequiredData(String mac, int level){
SQLiteDatabase db = this.getWritableDatabase();
Cursor res = db.rawQuery("SELECT BSSID, RSSI FROM TABLE_NAME WHERE BSSID =? AND RSSI=?", new String[] {mac, level});
return res;
}
scan part:
class WifiReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
sb = new StringBuilder();
Comparator<ScanResult> comparator = new Comparator<ScanResult>() {
#Override
public int compare(ScanResult o1, ScanResult o2) {
return (o1.level>o2.level ? -1 : (o1.level==o2.level ? 0 : 1));
}
};
lista = wifiManager.getScanResults();
Collections.sort(lista, comparator);
for (int i = 0; i < lista.size(); i++) {
scanResult = wifiManager.getScanResults().get(i);
sb.append(new Integer(i + 1).toString() + ". " + (lista.get(i)).SSID + " " + (lista.get(i)).BSSID + " " + (lista.get(i)).level + "\n");
boolean isInserted = myDb.insertData(lista.get(i).SSID.toString(), lista.get(i).BSSID.toString(), lista.get(i).level);
if (isInserted = true)
Toast.makeText(MainActivity.this, "Data inserted", Toast.LENGTH_LONG).show();
else
Toast.makeText(MainActivity.this, "Data not inserted", Toast.LENGTH_LONG).show();
}
for (int i=0; i<4; i++)
{
scanResult = wifiManager.getScanResults().get(i);
match = myDb.getRequiredData(lista.get(i).BSSID.toString(), lista.get(i).level);
}
Log.i("match values: ", DatabaseUtils.dumpCursorToString(match));
txt.setText(sb);
wifiManager.startScan();
}
}
Here is what match contains:
2018-12-10 16:36:26.334 13347-13347/com.example.maciek.wifiscann I/match values:: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor#e1a86d1
0 {
BSSID=f4:c5:ed:5c:s6:20
RSSI=-69
}
1 {
BSSID=f4:c5:ed:5c:s6:20
RSSI=-69
}
2 {
BSSID=f4:c5:ed:5c:s6:20
RSSI=-69
}
3 {
BSSID=f4:c5:ed:5c:s6:20
RSSI=-69
}
4 {
BSSID=f4:c5:ed:5c:s6:20
RSSI=-69
}
5 {
BSSID=f4:c5:ed:5c:s6:20
RSSI=-69
}
<<<<<
To get the 3 rows with the closest values to 60 in column LEVEL:
SELECT * FROM tablename ORDER BY ABS(LEVEL - 60), LEVEL LIMIT 3
For the 2nd part of your question, you should provide sample data of the table. Edit:
From the sample data that you posted I don't see a column RSSI, but if it exists in the table then the SELECT statement is ok.
Change the 2nd parameter of rawQuery() to:
new String[] {mac, String.valueOf(level)}
because level is int.
In onReceive() you use myDb. I don't know how you initialize it.
If the app crashes you must copy the log, the part that identifies the problem and post it.
First thing I would like to know if it is possible to search through
the database to get all the records with the LEVEL value equal or
close to 60, for instance 59,58,61.
SELECT * FROM your_table WHERE level BETWEEN 59 AND 61;
where your_table is the respective table name.
Note if levels are negative (as per example data) then BETWEEN requires the lowest value first so it would be BETWEEN -61 AND -59.
Secondly, is there a way to query the database to return all the
records with the same MAC addresses and RSSI values as from the 3 best
scan result? If so, how would that query look like?
SELECT * FROM your_table WHERE your_mac_address_column = 'the_mac_address_value' AND RSSI = 'the_rssi_value' ORDER BY LEVEL DESC LIMIT 3
Note the above assumes that the MAC address is stored in a column (if NOT then cannot be done unless the mac address can be correlated to a column).
Assumes best LEVEL is lowest so -1 is better than -60 (if not then use ASC instead of DESC)
Again your_table, your_mac_address_column, the_mac_address_value and the_rssi_value would be replaced accordingly with actual values (note that strings should be in single quotes).

BigTable CheckAndPut CompareOp.Greater always Puts

The checkAndPut method appears to always Put the value regardless of the GREATER compareOp. The code below illustrates this issue since the value "a" is lexicographically smaller than "bbbbbb", one would expect that a CheckAndPut would not put in the scenario, however it does.
I am using
<groupId>com.google.cloud.bigtable</groupId>
<artifactId>bigtable-hbase-1.x-hadoop</artifactId>
<version>1.3.0</version>
Below is the code:
byte[] key = "key17".getBytes();
byte[] smaller = "a".getBytes();
byte[] larger = "bbbbbb".getBytes();
byte[] COL_FAMILY = "family".getBytes();
byte[] COL_QUAL = "qual".getBytes();
/** if null, we set it here **/
boolean doRun = table.checkAndPut(key, COL_FAMILY, COL_QUAL, null,
new Put(key).addColumn(COL_FAMILY, COL_QUAL, smaller));
System.out.println("WRITE OCCURRED: " + doRun);
Result rr = table.get(new Get(key).addFamily(COL_FAMILY));
byte[] res = rr.getValue(COL_FAMILY, COL_QUAL);
System.out.println("BEFORE STORED VAL: " + new String(res));
// should NOT put and return false since "a" is NOT greater than
// "bbbbbb"
doRun = table.checkAndPut(key, COL_FAMILY, COL_QUAL, CompareOp.GREATER, larger,
new Put(key).addColumn(COL_FAMILY, COL_QUAL, larger));
rr = table.get(new Get(key).addFamily(COL_FAMILY));
res = rr.getValue(COL_FAMILY, COL_QUAL);
System.out.println("AFTER STORED VAL: " + new String(res));
System.out.println("WRITE OCCURRED: " + doRun);
Output
WRITE OCCURRED: true
BEFORE STORED VAL: a
AFTER STORED VAL: bbbbbb
WRITE OCCURRED: true
After checking the HBASE docs, the example provided clarified what was actually being compared
GREATER operator means expected value > existing <=> add the put.
I had interpreted the opposite, so there is no issue.

Use null values to pad the ValueRange for values().update

Google Sheets API v4 is not passing null values as part of a ValueRange.
When a ValueRange is created with the following values:
"Test A", "Test B", null, null, "Test E"
with the range:
"A1:E1"
I get the following result:
A B C D E
1 Test A Test B Test E
But the expected result is:
A B C D E
1 Test A Test B Test E
The API used is spreadsheets().values().update(ID, range, ValueRange)
One possible solution is setting ValueInputOption to "USER_ENTERED" if not done already, however the problem still persists.
Here is a SSCCE in Java:
public static void test(Sheets service, String id) {
String range = "A1:E1";
List<List<Object>> values = Arrays.asList(Arrays.asList("Test A", "Test B", null, null, "Test E"));
ValueRange vRange = new ValueRange();
vRange.setRange(range);
vRange.setValues(values);
try {
System.out.println(service.spreadsheets().values().update(id, range, vRange)
.setValueInputOption("USER_ENTERED").execute().toPrettyString());
} catch (IOException e) {
e.printStackTrace();
}
}
And the result:
{
"spreadsheetId" : "[id]",
"updatedCells" : 3,
"updatedColumns" : 3,
"updatedRange" : "Sheet1!A1:C1",
"updatedRows" : 1
}
What changes need to be made to get null recognized, so that E will end up in the E column instead of the null values being glanced over?
I ran through a similar problem.
You can use Data.NULL_STRING (from com.google.api.client.util) instead of a native java null.
Fixed it for me